題干與適用場景
一個管理後台需要確認刪除操作,並在對話框內填寫原因。產品希望部分情境必須點擊明確按鈕才能關閉,部分情境允許 Escape 或點擊外部關閉。請使用原生 dialog 元素設計關閉策略,確保鍵盤、觸控和表單驗證路徑都可用。
MDN 將 closedby 分為 none、closerequest 和 any:分別限制為開發者機制、平台關閉請求加開發者機制、或三種方式全部允許。模態對話框使用 showModal(),非模態使用 show()。
面試官考察點
觀察候選人能否把業務風險映射到關閉策略,區分 modal 與 popover,理解 Escape、light dismiss、close()、requestClose()、method=dialog、初始焦點與表單驗證互動。
回答前需要釐清的問題
- 刪除確認是否允許誤觸關閉,未儲存輸入是否必須保留?
- Escape 和點擊外部是否等同取消,還是需要二次確認?
- 表單驗證失敗時,關閉按鈕是否應繞過驗證?
- 不支援
closedby的瀏覽器是否需要一致體驗? - 對話框關閉後,焦點應回到哪個觸發控制?
30 秒回答框架
「危險操作預設使用 showModal() 和 closedby="closerequest" 或 none,保留明確取消/確認按鈕;非破壞性提示可使用 any 或 popover="auto" 的輕量關閉。表單用 method="dialog" 管理結果,取消按鈕按需要加 formnovalidate。開啟時把焦點放到標題、第一個輸入或取消按鈕,關閉後恢復觸發器焦點。檢測能力並在舊瀏覽器回退到明確按鈕和手動事件處理。」
分步深入解答
第一步:選擇 modal 或 non-modal
showModal() 會讓其他頁面內容 inert,適合刪除確認和必須完成的工作;show() 不阻塞頁面,適合輔助面板。不要只切換 open 屬性模擬 modal,因為焦點和背景互動語意不會自動建立。
第二步:把風險映射到 closedby
closedby="none" 只允許開發者指定的關閉機制,適合不可逆確認;closerequest 允許 Escape 或平台關閉請求,也保留按鈕;any 額外允許點擊外部的 light dismiss,適合低風險資訊面板。
<dialog id="delete-dialog" closedby="closerequest">
<form method="dialog">
<button value="cancel">取消</button>
<button value="confirm">刪除</button>
</form>
</dialog>第三步:處理 Escape、request-close 與 close
Escape 或平台手勢屬於 close request;requestClose() 可觸發請求關閉流程,應用程式可以攔截並確認;close() 直接關閉並可設定 returnValue。不要把 Escape 靜默當作已確認刪除。
第四步:管理焦點與恢復位置
開啟 modal 後將焦點放在最合適的輸入、取消按鈕或標題;不要對 dialog 元素加 tabindex。關閉時把焦點還給觸發按鈕,若觸發器已移除則選擇穩定的替代位置。
第五步:處理 form method=dialog
method="dialog" 會關閉對話框並保存表單控制狀態,不會提交網路請求;按鈕的 value 會進入 returnValue。若取消按鈕應繞過必填驗證,使用 formnovalidate 或明確 close(),避免使用者被困在表單中。
第六步:區分 dialog 與 Popover
Popover 是非模態 top layer;popover="auto" 支援 light dismiss,manual 不會自動關閉且可同時顯示多個。不要把 popover 當作刪除確認的 modal 替代品,也不要把 modal 的 inert 期待套在非模態元素上。
第七步:能力檢測與降級
檢測 HTMLDialogElement、showModal 和 closedby 相關能力;不支援時提供明確按鈕、手動 Escape 處理、焦點管理和背景阻塞。降級路徑仍需保留相同的取消與確認語意。
第八步:驗證關閉與可存取性
測試鍵盤 Tab、Escape、外部點擊、觸控、表單驗證失敗、重複開啟、多個 modal、瀏覽器返回手勢和舊瀏覽器。記錄關閉原因(confirm、cancel、escape、light-dismiss),並用螢幕閱讀器驗證名稱、狀態和焦點移動。
高品質示範回答
「刪除確認使用 showModal(),危險情境預設 closedby="closerequest",最嚴格時用 none,保留明確取消按鈕。低風險提示才使用 any 或 popover="auto"。表單透過 method="dialog" 返回結果,取消按鈕按需要繞過驗證。開啟時設定初始焦點,關閉後恢復觸發器焦點;舊瀏覽器檢測能力並回退到明確按鈕、Escape 和手動焦點管理。測試所有關閉來源和輔助技術。」
常見錯誤
- 所有 dialog 都用 any → 刪除操作可能被誤觸關閉 → 按風險選擇 closedby。
- 對 dialog 加 tabindex → 焦點語意錯誤 → 聚焦內部可互動元素或標題。
- 把 close 當 requestClose → 跳過確認邏輯 → 區分請求關閉與直接關閉。
- form method=dialog 遇到驗證失敗無法取消 → 使用者被困住 → 取消按鈕使用 formnovalidate 或 close。
- 用 popover 代替 modal → 背景仍可互動 → 需要 inert 時使用 showModal。
- 只測滑鼠 → 鍵盤和觸控使用者無法退出 → 覆蓋 Escape、Tab、外部點擊和螢幕閱讀器。
追問及應對
追問一:closerequest 與 none 的核心差異是什麼?
closerequest 允許 Escape 或平台關閉請求,none 只允許開發者指定機制;不可逆操作通常需要 none 或額外確認。
追問二:為什麼 showModal 比 open 屬性可靠?
它建立模態狀態、背景 inert 和瀏覽器預設焦點行為;單獨設定 open 只顯示非模態對話框。
追問三:Escape 關閉時 returnValue 會怎樣?
除非應用程式主動設定,Escape 不等同某個提交按鈕的 value;關閉原因應單獨記錄為 escape 或 request-close。
追問四:如何讓取消按鈕繞過必填驗證?
對取消按鈕使用 formnovalidate,或在事件中呼叫 close("cancel");確認按鈕仍保留正常驗證。
追問五:舊瀏覽器如何降級?
提供明確開啟/關閉按鈕,手動管理 Escape、焦點陷阱、背景阻塞和關閉原因;能力恢復後再使用原生屬性,契約測試保持一致。