动态更新的实时区域
向屏幕阅读器播报异步状态变化(toast、搜索结果、表单错误)。
无障碍进阶
为什么
toast 出现或搜索结果更新时,视力用户即时见。屏幕阅读器用户无实时区域则听不到 — 以为啥也没发生。
WebAIM Million 2024 报告发现 96.3% 的首页存在可检测的 WCAG 失败。2023 年 ADA 相关诉讼超过 4,500 起,使无障碍成为法律和伦理必需,而非可有可无。
何时使用
任何用户需知的异步更新用实时区域:toast、即输即搜结果、表单校验错误、加载→完成转换、购物车数变。不要将无障碍视为最终阶段清单 — 事后改造成本是内置的 10 倍。
避免仅依赖自动扫描器;它们只能发现约 40% 的问题。
如何做
1. polite — aria-live="polite" 非紧急(搜索结果、购物车数);用户暂停时播报
2. assertive — aria-live="assertive" 紧急(错误、会话过期);打断当前语音
3. atomic — aria-atomic="true" 播报整区域,非仅变节点
4. relevant — aria-relevant="additions text"(默认)控制何变化播报
5. 预声明 — 初始 HTML 放空实时区域,后更新内容;动态加 aria-live 不可靠
6. 角色 — role="status" 隐含 polite+atomic;role="alert" 隐含 assertive+atomic
7. 测试 — NVDA/VoiceOver:触发更新,验证不移焦点即播报
示例
<!-- 布局中预声明空实时区域 -->
<div id="toast-region" role="status" aria-live="polite" aria-atomic="true" class="sr-only"></div>
<div id="alert-region" role="alert" aria-live="assertive" aria-atomic="true" class="sr-only"></div>
// Toast — 写入 polite 区域
function showToast(msg: string) {
document.getElementById("toast-region")!.textContent = msg;
}
showToast("已存收藏");
// NVDA:"已存收藏"(当前语音完后)
// 搜索结果 — 播报数量
function onResults(items: Item[]) {
document.getElementById("toast-region")!.textContent =
`${items.length} 个结果可用`;
}
// 表单错误 — assertive,打断
function showError(msg: string) {
document.getElementById("alert-region")!.textContent = msg;
}
showError("邮箱必填");
// NVDA:"邮箱必填"(打断)
常见问题
- ⚠动态加 aria-live — 屏幕阅读器不拾取,更新静默
- ⚠处处 assertive — 持续打断,用户听不到其他
- ⚠移焦点而非用实时区域 — 让用户迷失,丢位置
最佳实践
- ✓静态 HTML 预声明实时区域 — 更新内容非属性
- ✓默认 polite — assertive 留给需即时注意的真错误
- ✓用 role=status / role=alert — 隐含对的 live+atomic 组合,少记