SC 4.1.3 级别 AA WCAG 2.1
状态消息
状态消息——确认、错误、进度更新、搜索结果数量——必须在不移动焦点的情况下向辅助技术宣告。在已存在于 DOM 中的区域上使用 role=status、role=alert 或 aria-live。
要求内容
当页面告知用户发生了某件事情——“商品已加入购物车”、“找到 3 条结果”、“连接已断开”、“正在保存……”——该消息必须在不强制移动焦点的情况下传达给屏幕阅读器用户。该信息必须以编程方式确定为状态,以便辅助技术能够获取并宣告它。
WCAG 列出了三类状态消息:
- 成功 / 完成 — “个人资料已保存”、“电子邮件已发送”。
- 操作结果 — “12 条结果”、“未找到匹配项”。
- 应用状态 — “正在保存……”、“正在重新连接”、“上传 40%“。
如何满足
- 在页面加载时将实时区域放入 DOM——一个带有
role="status"(礼貌)或role="alert"(强制)的空元素。更新时,将新文本写入其中。 - 对于非关键性确认和结果数量,使用
role="status"或aria-live="polite"。屏幕阅读器等待用户空闲后再宣告。 - 对于关键消息——表单提交错误、连接断开、会话即将过期——使用
role="alert"或aria-live="assertive"。宣告会打断当前操作。 - Toast 通知需要实时区域。Toast 容器应在 Toast 出现之前就存在于 DOM 中,并设置好
aria-live,然后将 Toast 文本作为子节点注入。 - 如果希望在每次更新时重新读取整个区域而非仅读取变更节点,将
aria-live与aria-atomic="true"搭配使用。 - 对于表单错误,内联错误消息可使用
role="alert",或将焦点移至表单顶部的摘要处——但不要两者同时使用;用户只需听到一次。
常见失误
- Toast 通知渲染到 Toast 出现之前不存在的元素中——实时区域监听器从未附加,什么都不会被宣告。
- 成功横幅被插入,但位于任何
aria-live区域之外——对有视力的用户可见,对屏幕阅读器用户不可见。 - 搜索结果数量(“显示 340 条中的 12 条”)在过滤后静默更新。用户返回结果时不确定是否发生了任何变化。
- 表单验证更新了带有
<span class="error">的内联元素,但没有role="alert"或aria-live——错误出现了,屏幕阅读器保持沉默。 - 对每条状态更新都使用
aria-live="assertive"。持续打断;屏幕阅读器用户禁用该页面。 - 将焦点移至非标题、非交互性的状态消息——焦点落在不可聚焦的元素上,键盘用户迷失方向。
- “正在保存……”/“已保存”指示器仅切换 CSS 类,但从不更新实时区域内的任何文本内容。
为何重要
状态消息是现代单页应用的薄弱环节。页面从不重新加载,焦点很少移动,因此用户唯一能感知到发生了某件事的信号是视觉变化——对于不盯着屏幕的人毫无用处。4.1.3 强制实现平等:如果有视力的用户能看到”已加入购物车”,屏幕阅读器用户就应该能听到它。修复方案几乎总是廉价的(一个实时区域,在更新时填充内容),而缺少它几乎总是破坏核心流程——结账、搜索、表单提交、实时聊天。