具代表性的面試主題

前端面試:如何用 dialog closedby 設計可存取的關閉策略?

前端困難
Offer.cc 編輯團隊發佈 更新

題幹

請設計一個包含危險操作確認和表單驗證的 HTML dialog。要求比較 closedby=none、closerequest、any,說明 showModal 與 show、Popover API、焦點、關閉原因和舊瀏覽器降級。

題幹與適用場景

一個管理後台需要確認刪除操作,並在對話框內填寫原因。產品希望部分情境必須點擊明確按鈕才能關閉,部分情境允許 Escape 或點擊外部關閉。請使用原生 dialog 元素設計關閉策略,確保鍵盤、觸控和表單驗證路徑都可用。

MDN 將 closedby 分為 nonecloserequestany:分別限制為開發者機制、平台關閉請求加開發者機制、或三種方式全部允許。模態對話框使用 showModal(),非模態使用 show()

面試官考察點

觀察候選人能否把業務風險映射到關閉策略,區分 modal 與 popover,理解 Escape、light dismiss、close()requestClose()method=dialog、初始焦點與表單驗證互動。

回答前需要釐清的問題

  • 刪除確認是否允許誤觸關閉,未儲存輸入是否必須保留?
  • Escape 和點擊外部是否等同取消,還是需要二次確認?
  • 表單驗證失敗時,關閉按鈕是否應繞過驗證?
  • 不支援 closedby 的瀏覽器是否需要一致體驗?
  • 對話框關閉後,焦點應回到哪個觸發控制?

30 秒回答框架

「危險操作預設使用 showModal()closedby="closerequest"none,保留明確取消/確認按鈕;非破壞性提示可使用 anypopover="auto" 的輕量關閉。表單用 method="dialog" 管理結果,取消按鈕按需要加 formnovalidate。開啟時把焦點放到標題、第一個輸入或取消按鈕,關閉後恢復觸發器焦點。檢測能力並在舊瀏覽器回退到明確按鈕和手動事件處理。」

分步深入解答

第一步:選擇 modal 或 non-modal

showModal() 會讓其他頁面內容 inert,適合刪除確認和必須完成的工作;show() 不阻塞頁面,適合輔助面板。不要只切換 open 屬性模擬 modal,因為焦點和背景互動語意不會自動建立。

第二步:把風險映射到 closedby

closedby="none" 只允許開發者指定的關閉機制,適合不可逆確認;closerequest 允許 Escape 或平台關閉請求,也保留按鈕;any 額外允許點擊外部的 light dismiss,適合低風險資訊面板。

html
<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 期待套在非模態元素上。

第七步:能力檢測與降級

檢測 HTMLDialogElementshowModalclosedby 相關能力;不支援時提供明確按鈕、手動 Escape 處理、焦點管理和背景阻塞。降級路徑仍需保留相同的取消與確認語意。

第八步:驗證關閉與可存取性

測試鍵盤 Tab、Escape、外部點擊、觸控、表單驗證失敗、重複開啟、多個 modal、瀏覽器返回手勢和舊瀏覽器。記錄關閉原因(confirm、cancel、escape、light-dismiss),並用螢幕閱讀器驗證名稱、狀態和焦點移動。

高品質示範回答

「刪除確認使用 showModal(),危險情境預設 closedby="closerequest",最嚴格時用 none,保留明確取消按鈕。低風險提示才使用 anypopover="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、焦點陷阱、背景阻塞和關閉原因;能力恢復後再使用原生屬性,契約測試保持一致。

公開來源

同類題目