題幹與適用場景
搜尋、儲存與上傳不會導覽,焦點也不會離開目前控制項。視覺介面顯示「正在儲存」「已儲存」或「儲存失敗」,但非同步變更可能發生在焦點之外。目標是讓輔助技術取得簡短、可行動且不打斷輸入的狀態,並保留鍵盤、無腳本與網路失敗時的可用性。
面試官考察點
面試官會看你能否區分狀態訊息、錯誤與必須立即處理的警告;是否知道 live region 要在內容變更前存在;是否能控制重複播報、競態回應與焦點移動;是否把伺服器結果、重試動作與自動化測試納入設計。
回答前需要釐清的問題
- 哪些更新只是建議性狀態,哪些會阻止使用者繼續操作?
- 使用者是否可能連續觸發多個請求,回應是否帶序號?
- 失敗是否有就地重試、復原或查看詳情的動作?
- 儲存完成後焦點應留在編輯控制項,還是移到錯誤詳情?
- 是否支援普通表單提交、鍵盤、縮放與高對比模式?
30 秒回答框架
我會在初始 DOM 放置空的 role="status" 區域,把一般進度與完成訊息寫入其中;它隱含禮貌級播報,不搶走輸入焦點。需要處理的錯誤在相關控制項旁提供文字與動作,真正緊急的情況才使用 alert。訊息去重並節流,回應完成時比較序號,舊回應不能覆蓋新狀態。最後以鍵盤、讀屏、網路失敗與連續請求測試。
分步深入解答
第一步:建立狀態模型
分開 idle、pending、success、error 與 cancelled。狀態區只說目前結果,例如「正在儲存」「已儲存」「儲存失敗,可重試」,不要播報內部請求名或每個百分比。錯誤另存欄位、重試動作與關聯控制項。
第二步:預先存在 live region
W3C 與 MDN 建議在更新前建立 live region。role="status" 適合不應打斷使用者的建議訊息,並隱含 aria-live="polite";不要每次變更才掛上屬性。
<p id="save-status" role="status" aria-atomic="true"></p>
<button type="submit">儲存</button>第三步:控制播報節奏
相同文字不重複寫入;輸入搜尋字詞時不要逐字播報。高頻結果可去抖,只播報「結果已更新」;完成、失敗與需要行動的狀態要說出下一步。aria-live="assertive" 會打斷語音,只保留給真正緊急的訊息。
第四步:處理競態與取消
每個請求分配遞增序號或 AbortController。回應只有在仍是最新序號且元件仍掛載時才能更新狀態區。取消的請求不應播報失敗;新請求開始時可替換舊的等待訊息。
第五步:安排焦點與錯誤關聯
一般成功訊息不移動焦點,讓使用者繼續操作。失敗時在控制項旁顯示文字並以 aria-describedby 關聯;頁面級錯誤提供可聚焦的摘要與重試按鈕。狀態播報與焦點移動只選主要通道,避免重複朗讀。
第六步:保留降級路徑
伺服器仍接受普通表單提交並回傳結構化結果。JavaScript 失敗、逾時或權限過期時,文字要說明狀態與動作,例如「儲存失敗,請重試」,不能只留下旋轉圖示。失敗不應清空輸入。
設計取捨與邊界
status、alert 與可見錯誤
status 用於禮貌通知;alert 會立即打斷,不應取代所有錯誤。可見錯誤仍要存在,因為讀屏播報不是唯一感知方式。顏色、圖示與動畫只能補充文字。
進度細節與噪聲
上傳百分比可在視覺區顯示,但讀屏只需在開始、階段變化與完成時取得摘要。每 1% 更新 live region 會製造噪聲,應透過真實裝置與使用者測試校準節流。
落地計畫與證據
元件與契約
統一狀態訊息元件的文字、去重與序號檢查;API 回傳 success、retryable 與 fieldErrors 等結構化欄位。元件只負責體驗,不把伺服器錯誤碼直接顯示給使用者。
驗證矩陣
以鍵盤完成搜尋、儲存與上傳,在 NVDA、VoiceOver 等讀屏下確認只播報一次。覆蓋慢網、逾時、連點、亂序回應、取消、頁面卸載、強制顏色與 200% 縮放。自動化斷言 DOM 語意,人工測試確認語音順序。
常見誤區與追問
誤區:動態新增 aria-live
屬性與節點應先存在,再更新文字;否則部分輔助技術可能錯過首次變更。
誤區:成功後把焦點移到狀態文字
這會打斷目前輸入。除非使用者必須處理錯誤,否則讓焦點留在原控制項。
誤區:所有錯誤都使用 assertive
高優先級播報會打斷閱讀。先判斷是否真的需要立即注意,並提供可見、可操作錯誤。
追問:如何避免舊回應覆蓋新回應?
比較遞增序號,或同時使用取消與序號檢查;取消不能保證回呼不會執行。
追問:如何證明設計有效?
提供讀屏觀察、鍵盤流程、網路故障與競態場景證據,不只展示掃描通過。