대화상자 가볍게 닫기

웹 기능: <dialog closedby>

최신 모달 대화상자는 '가벼운 닫기'를 지원하는 경우가 많으므로 사용자가 배경 (대화상자 외부 영역)을 클릭하거나 탭하여 대화상자를 닫을 수 있습니다. closedby 속성은 커스텀 JavaScript 없이 이 동작을 사용 설정하는 선언적 방법을 제공합니다.

구현

가벼운 닫기를 사용 설정하려면 다음 단계를 따르세요.

  1. <dialog> 요소에 closedby="any"를 추가합니다.
  2. dialog.showModal()를 사용하여 대화상자를 엽니다.

속성 값

  • any: 가벼운 닫기 (배경 클릭), '닫기 요청'(Esc 키), 개발자 메커니즘 (예: dialog.close())을 사용 설정합니다.
  • closerequest: '요청 종료' 및 개발자 메커니즘만 사용 설정합니다. 이는 모달 대화상자의 기본값입니다.
  • none: 개발자 메커니즘만 대화상자를 닫을 수 있습니다.

배경 스타일 지정

showModal()를 사용하여 대화상자를 모달로 열면 브라우저에서 ::backdrop 의사 요소를 생성합니다. 이 배경은 전체 표시 영역을 덮고 대화상자 바로 뒤에 있습니다.

/* Style the backdrop to indicate the dialog is modal */
dialog::backdrop {
  background-color: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px); /* Optional: add blur for modern browsers */
}

예

<!-- MANDATORY: Use closedby="any" to enable light-dismiss behavior -->
<dialog id="myDialog" closedby="any" aria-labelledby="dialogTitle">
  <form method="dialog">
    <h2 id="dialogTitle">Feedback</h2>
    <p>Click outside this box or press Esc to dismiss.</p>
    <button type="submit">Close</button>
  </form>
</dialog>

<button onclick="document.getElementById('myDialog').showModal()">Open Dialog</button>

제약 조건 및 접근성

  • 필수: closedby="any"를 사용하여 선언적으로 가벼운 닫기를 사용 설정합니다.
  • 필수: 항상 showModal()를 사용하여 모달 대화상자를 엽니다. 이렇게 하면 대화상자가 최상위 레이어에 있고 포커스가 트랩되며 Esc 키가 처리됩니다.
  • 권장사항: aria-labelledby 또는 aria-label를 사용하여 대화상자에 액세스 가능한 이름을 제공합니다.
  • 금지사항: 비모달 대화상자(show()로 열림)에는 closedby를 사용하지 마세요. 배경이 없고 라이트 닫기를 트리거하지 않기 때문입니다.
  • 금지사항: 닫기 전에 실행되어야 하는 중요한 로직에 click 이벤트를 사용하지 마세요. 대신 close 또는 cancel 이벤트를 수신 대기하세요.

대체 전략

필수: 아직 closedby를 지원하지 않는 브라우저의 경우 다음 스크립트를 사용하여 대화상자 콘텐츠 경계 외부에서 클릭이 발생했는지 확인하여 가벼운 닫기의 대체 기능을 구현해야 합니다.

const dialog = document.querySelector('dialog');

// Fallback for browsers without closedby support
if (!('closedBy' in HTMLDialogElement.prototype)) {
  dialog.addEventListener('click', (event) => {
    // 1. When clicking the backdrop, the event target is the dialog element itself.
    // Ignore clicks where the target is a child element inside the dialog.
    if (event.target !== dialog) return;

    // 2. Check if the click coordinates fall within the dialog's content box.
    // This distinguishes between a click on the backdrop vs a click on the dialog's background/padding.
    const rect = dialog.getBoundingClientRect();
    const isDialogContent = (
      rect.top <= event.clientY &&
      event.clientY <= rect.top + rect.height &&
      rect.left <= event.clientX &&
      event.clientX <= rect.left + rect.width
    );

    if (isDialogContent) return;

    // 3. Since the click was outside the content area (on the backdrop), manually close the dialog.
    dialog.close();
  });
}