題幹與適用場景
你要實作一個設定按鈕,既能開啟對話框,也能在不依賴元件狀態同步的情況下關閉它。請說明如何用 command 和 commandfor 建立宣告式控制,並處理自訂命令、鍵盤可達性和舊瀏覽器降級。
Invoker Commands API 讓按鈕透過目標元素的 ID 觸發預定義動作或派發自訂 command 事件。它把「誰觸發」與「哪個元素執行」寫進 HTML,適合對話框、Popover 等互動,但不能取代狀態機、權限檢查或不支援瀏覽器的備用路徑。
面試官考察點
面試官會關注你是否理解 commandfor 必須指向同一棵樹中的目標、內建命令與自訂命令的區別、事件取消與預設行為、鍵盤和焦點管理,以及漸進增強而非把新屬性當成唯一執行期保障。
澄清問題
先確認目標是模態對話框、非模態 Popover 還是普通元素,按鈕是否位於同一 DOM 樹,互動是否需要提交前驗證,以及目標瀏覽器支援範圍。再確認開啟後焦點落點、Escape 行為、關閉後的焦點返回和自訂命令是否需要業務權限判斷。
30 秒回答框架
「我會讓按鈕用 commandfor 指向目標元素,用內建命令表達顯示、隱藏或切換;業務動作用自訂命令事件,由目標或控制器執行驗證並可取消預設行為。原生按鈕天然可用鍵盤操作,但我仍會驗證焦點進入、Escape、關閉後返回和讀屏名稱。對不支援的瀏覽器保留 JavaScript 事件監聽或元件 fallback,並用同一狀態機測試原生與降級路徑。」
分步驟深入解答
第一步:建立按鈕到目標的宣告式關係
commandfor 的值是目標元素的 ID,按鈕與目標必須在同一棵樹中。按鈕的 command 值決定觸發哪個動作;這讓靜態 HTML 也能表達控制關係,減少手動查詢和狀態同步程式碼。
第二步:優先使用內建命令
對話框和 Popover 有瀏覽器定義的命令,例如顯示、隱藏和切換。內建命令由目標元素的原生行為處理,能與頂層、light dismiss 和焦點規則協同。不要用自訂字串重複實作已有語義。
<button commandfor="settings" command="show-modal">
開啟設定
</button>
<dialog id="settings">
<button commandfor="settings" command="close">關閉</button>
</dialog>第三步:區分自訂命令事件
以 -- 開頭的自訂命令會派發 command 事件,事件包含目標和命令值。處理器應驗證來源和目前狀態,必要時呼叫 preventDefault(),再執行業務動作。自訂命令不會自動改變目標的顯示狀態。
第四步:把權限和驗證留在業務層
宣告式觸發只表達意圖,不能繞過權限、表單驗證或非同步儲存。目標控制器應在執行命令前確認使用者權限、資料版本和目前狀態,失敗時給出可恢復錯誤,不讓按鈕 HTML 成為安全邊界。
第五步:設計焦點與鍵盤行為
使用原生對話框或 Popover 時,核對開啟後的初始焦點、Escape 關閉、關閉後焦點返回和讀屏名稱。普通元素不會自動獲得這些語義,若命令目標不是原生互動元素,就需要額外實作可存取的鍵盤模型。
第六步:處理事件取消與競態
多個按鈕可能同時發出命令。事件處理器應檢查目標目前狀態,避免重複開啟、重複提交或關閉後又被舊非同步結果開啟。將非同步業務狀態與可見狀態分離,確保失敗不會留下錯誤的 aria 或焦點狀態。
第七步:提供舊瀏覽器降級
不支援 commandfor 的瀏覽器會忽略這些屬性,頁面必須保留普通按鈕的 JavaScript 監聽或元件繫結。降級路徑應重用同一命令處理函式和可存取性測試,不要複製一套行為造成兩種實作漂移。
第八步:測試原生和降級路徑
測試滑鼠、鍵盤、讀屏、Escape、焦點返回、重複命令、取消預設行為、權限拒絕和非同步失敗。用瀏覽器相容矩陣確認內建命令、自訂事件與目標型別的支援,不把 MDN 的 Baseline 標籤當作所有執行環境的保證。
高品質示例答案
我會讓靜態按鈕透過 commandfor 指向同一棵樹中的對話框,並用內建 show-modal 和 close 命令表達基礎行為。需要業務邏輯時使用自訂命令事件,在處理器中檢查權限、表單版本和目前狀態,必要時取消預設行為;自訂命令本身不會自動切換顯示狀態。原生對話框負責模態語義,但我仍會驗證焦點、Escape、關閉後返回和讀屏名稱。舊瀏覽器忽略新屬性,所以保留同一命令處理函式的事件監聽 fallback。最後用鍵盤、讀屏、並行點擊、非同步失敗和瀏覽器矩陣測試兩條路徑。
常見誤區
認為 commandfor 會跨 Shadow DOM 或文件尋找目標
規範要求目標 ID 在同一棵樹中。跨樹或跨文件控制需要明確元件協定或訊息通訊,不能只改變 ID。
認為所有自訂命令都有預設行為
自訂命令主要派發事件,不會自動開啟、關閉或提交目標。處理器必須實作狀態改變,並明確何時取消預設行為。
只驗證滑鼠點擊
按鈕的鍵盤可達性、焦點順序、Escape 和讀屏回饋同樣是互動契約。視覺上能開啟不代表可存取行為完整。
延伸追問與參考答案
目標元素動態替換後,commandfor 需要重新繫結嗎?
只要新元素使用相同 ID 且仍在同一棵樹中,宣告關係可繼續工作,但控制器要重置焦點和業務狀態。若 ID 重複或目標暫時不存在,應阻止命令並記錄可診斷狀態。
自訂命令觸發非同步儲存時,如何防止重複提交?
在處理器中用請求狀態或冪等鍵鎖定同一動作,按鈕進入忙碌狀態並保留取消或重試入口。非同步完成後再次確認目標仍存在,避免舊結果修改新會話。
何時應繼續使用 JavaScript 元件,而不是只用 HTML 命令?
當互動需要跨樹協調、複雜動畫、非同步狀態機或大量舊瀏覽器相容時,元件狀態更容易集中管理。仍可用 command 作為漸進增強入口,但不能犧牲可測試的業務邊界。