HTML command·commandfor 속성으로 JavaScript 없이 대화상자 열기

HTML command·commandfor 속성으로 대화상자와 팝오버를 선언적으로 제어하는 방법과 주의사항을 정리합니다.

10/2/2026

html

조회수 4

HTML command·commandfor 속성으로 JavaScript 없이 대화상자 열기

모달을 여는 버튼 하나를 만들 때도 보통 버튼을 찾고, click 리스너를 붙이고, 그 안에서 dialog.showModal()을 호출하는 코드가 필요했습니다. 화면에 버튼이 늘어날수록 이런 연결 코드가 반복되고, 스크립트가 로드되기 전에는 버튼이 동작하지 않는 문제도 생깁니다.

HTML 표준에 추가된 Invoker Commands는 <button>에 commandfor와 command 속성을 지정해 버튼이 다른 요소에 어떤 명령을 보낼지 선언하는 기능입니다. Chrome 135에서 처음 출시되었고, MDN 기준으로 2025년 12월 Baseline(Newly available)이 되었습니다.

핵심 개념

  • commandfor: 제어할 요소의 id를 지정합니다.

  • command: 그 요소에 보낼 명령을 지정합니다.

HTML 표준이 정의한 내장 명령은 다음과 같습니다.

  • show-modal: 대상 <dialog>를 모달로 엽니다.

  • close: 대상 <dialog>를 닫습니다.

  • request-close: 대상 <dialog>에 닫기를 요청합니다. 상대적으로 최근에 추가된 값이므로 대상 브라우저의 지원 여부를 따로 확인하세요.

  • show-popover, hide-popover, toggle-popover: 대상 popover 요소를 열거나 닫거나 토글합니다.

또한 --로 시작하는 값은 사용자 정의 명령으로 취급됩니다. 사용자 정의 명령은 브라우저가 별도 동작을 수행하지 않고, 대상 요소에 command 이벤트만 발생시킵니다. 이벤트 객체인 CommandEvent의 command 속성에는 명령 문자열이, source 속성에는 명령을 보낸 버튼이 들어 있습니다.

실무 예제 1: 확인 대화상자와 메뉴

다음 코드는 스크립트 없이 모달 대화상자와 팝오버 메뉴를 여닫습니다. HTML 파일로 저장해 지원 브라우저에서 열어 보세요.

<!doctype html>
<html lang="ko">
<meta charset="utf-8">
<title>Invoker Commands 예제</title>
<button type="button" commandfor="confirm" command="show-modal">삭제</button>

<dialog id="confirm" aria-labelledby="confirm-title">
  <h2 id="confirm-title">정말 삭제할까요?</h2>
  <p>삭제한 항목은 복구할 수 없습니다.</p>
  <button type="button" commandfor="confirm" command="close">취소</button>
</dialog>

<button type="button" commandfor="menu" command="toggle-popover">메뉴</button>
<div id="menu" popover>
  <a href="#profile">프로필</a>
  <a href="#settings">설정</a>
</div>
</html>

내장 명령은 대상 요소의 종류와 맞아야 동작합니다. 예를 들어 show-modal은 <dialog>, toggle-popover는 popover 속성이 있는 요소를 대상으로 합니다.

실무 예제 2: 사용자 정의 명령

내장 명령으로 표현할 수 없는 동작은 -- 접두사를 붙인 명령과 command 이벤트로 처리합니다. 버튼마다 리스너를 붙이지 않고 대상 요소 하나에서 명령을 받아 분기할 수 있습니다.

<!doctype html>
<html lang="ko">
<meta charset="utf-8">
<title>사용자 정의 명령 예제</title>
<p>
  <button type="button" commandfor="counter" command="--decrement">-1</button>
  <output id="counter" aria-live="polite">0</output>
  <button type="button" commandfor="counter" command="--increment">+1</button>
  <button type="button" commandfor="counter" command="--reset">초기화</button>
</p>
<script>
  const counter = document.getElementById("counter");

  counter.addEventListener("command", (event) => {
    const current = Number(counter.textContent);

    if (event.command === "--increment") {
      counter.textContent = current + 1;
    } else if (event.command === "--decrement") {
      counter.textContent = current - 1;
    } else if (event.command === "--reset") {
      counter.textContent = 0;
    }

    console.log("누른 버튼:", event.source.textContent);
  });
</script>
</html>

실무 예제 3: 기본 동작 취소하기

command 이벤트는 내장 동작이 실행되기 전에 대상 요소에서 발생하며 취소할 수 있습니다. preventDefault()를 호출하면 내장 동작이 실행되지 않습니다. 아래 코드에서 hasUnsavedChanges와 showUnsavedWarning은 애플리케이션에서 구현할 함수입니다.

const dialog = document.getElementById("confirm");

dialog.addEventListener("command", (event) => {
  if (event.command === "close" && hasUnsavedChanges()) {
    event.preventDefault(); // 기본 닫기 동작을 실행하지 않는다
    showUnsavedWarning();
  }
});

주의할 점

  • 폼 안의 버튼에는 type="button"을 명시하세요. 표준에서는 type이 없더라도 command나 commandfor가 있으면 제출 버튼으로 동작하지 않습니다. 하지만 이 기능을 지원하지 않는 브라우저는 폼 안의 type 없는 버튼을 제출 버튼으로 처리합니다.

  • Shadow DOM 경계를 넘을 수 없습니다. commandfor는 id로 대상을 찾으므로 다른 shadow root의 요소를 가리킬 수 없습니다. 이때는 JavaScript에서 button.commandForElement 속성에 요소를 직접 지정할 수 있습니다.

  • 지원하지 않는 브라우저를 고려하세요. Baseline Newly available은 최신 주요 브라우저에서 동작한다는 의미이며, 오래된 브라우저에서는 속성이 무시됩니다. 대상 사용자 환경에 따라 기능 감지 후 대체 처리를 두세요.

const supportsCommand = "command" in HTMLButtonElement.prototype;

if (!supportsCommand) {
  // 지원하지 않는 브라우저에서는 기존처럼 click 이벤트로 처리한다
  document.querySelectorAll("button[commandfor]").forEach((button) => {
    button.addEventListener("click", () => {
      const target = document.getElementById(button.getAttribute("commandfor"));
      const command = button.getAttribute("command");

      if (command === "show-modal") target.showModal();
      if (command === "close") target.close();
      if (command === "toggle-popover") target.togglePopover();
    });
  });
}

위 대체 코드는 예제에서 사용한 세 가지 명령만 처리하는 최소 구현입니다. 사용자 정의 명령까지 지원해야 한다면 별도 처리를 추가하거나 검증된 폴리필을 검토하세요.

정리

commandfor와 command는 "이 버튼이 어떤 요소에 무엇을 하는지"를 HTML에 선언하게 해 줍니다. 대화상자와 팝오버 같은 반복적인 연결 코드를 줄이고, 스크립트 로드 전에도 동작하는 UI를 만들 수 있습니다. 사용자 정의 명령과 취소 가능한 command 이벤트를 함께 쓰면 필요한 부분에만 JavaScript를 더할 수 있습니다. 실무에서는 폼 안 버튼의 type과 지원 범위를 함께 확인하세요.

참고 자료

댓글 0