题干与适用场景
页面中的搜索、保存、上传不会导航,也不会把焦点移走。视觉界面显示“正在保存”“已保存”或“保存失败”,但异步更新可能发生在当前焦点之外。目标是让辅助技术获得简短、可行动且不打断输入的状态,同时保留键盘、无脚本和网络失败时的可用性。
面试官考察点
面试官看你是否能区分状态消息、错误和必须立即处理的警告;是否知道 live region 要在内容变化前就存在;是否会控制重复播报、竞态响应和焦点移动;是否把服务端结果、可重试动作和自动化测试纳入设计,而不是只添加一个 aria-live 属性。
回答前需要澄清的问题
- 哪些更新是建议性状态,哪些会阻止用户继续操作?
- 用户是否可能连续触发多个请求,响应是否带有序号?
- 失败是否有就地重试、撤销或查看详情的动作?
- 保存完成后焦点应留在编辑控件,还是必须移到错误详情?
- 页面是否支持普通表单提交、键盘操作、缩放和高对比度模式?
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
高优先级播报会打断阅读。先判断是否真的需要立即注意,并提供可见、可操作的错误。
追问:如何避免旧响应覆盖新响应?
比较单调递增序号,或同时使用取消和序号检查;取消本身不能证明回调不会执行。
追问:如何证明设计有效?
给出读屏录音或人工观察、键盘流程、网络故障和竞态场景证据,而不是只展示自动化扫描通过。