ダイアログをライトディスミスする

ウェブ機能: <dialog closedby>

最新のモーダル ダイアログは多くの場合、「ライトディスミス」をサポートしています。これにより、ユーザーは背景(ダイアログの外側の領域)をクリックまたはタップしてダイアログを閉じることができます。closedby 属性を使用すると、カスタム JavaScript を使用せずにこの動作を有効にできます。

実装

ライトディスミスを有効にするには:

  1. closedby="any" を <dialog> 要素に追加します。
  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();
  });
}