輕觸關閉對話方塊

網頁功能: <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 為對話方塊提供無障礙元素名稱。
  • 請勿:將 closedby 用於非互動式對話方塊 (使用 show() 開啟),因為這類對話方塊沒有背景,也不會觸發輕觸關閉。
  • 請勿:將 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();
  });
}