状态 动态区域状态
aria-live
将区域标记为其更新应由辅助技术宣读、而无需移动焦点的区域。大多数情况选用 "polite",仅在内容确实紧急时选用 "assertive"。该区域必须在初始渲染时就存在于 DOM 中。
使用时机
当页面某部分异步更新,且希望屏幕阅读器用户能知晓此更新、同时不打断其当前阅读时使用。表单验证摘要、搜索结果数量、Toast 通知、聊天流消息和购物车计数器是典型场景。
有意识地选择礼貌级别:
aria-live="polite"——等到用户空闲后再宣读。几乎所有场景都应使用此值。状态消息、加载完毕的结果、已添加至购物车的商品。aria-live="assertive"——立即打断用户。仅用于真正紧急的信息——会话将在 30 秒后过期、表单提交失败、付款被拒绝。滥用会使页面令人反感。aria-live="off"(默认值)——不进行任何宣读。
原生角色 role="status"(隐式 polite)和 role="alert"(隐式 assertive)已将 aria-live 与合理的默认值绑定在一起。适用时优先选用这两个角色;在自定义容器上不适用时再使用 aria-live。
如何保持同步
关键规则:实时区域必须在初始渲染时就存在于 DOM 中。浏览器和辅助技术在节点首次出现在无障碍树时设置该区域的”监听”。如果你在同一个 JavaScript 执行周期内创建区域并注入内容,宣读往往会被错过。
正确模式:
<div id="status" aria-live="polite"></div>
在页面加载时渲染空容器,之后通过 JavaScript 向其写入文本,屏幕阅读器即宣读该变化。
其他规则:
- 通过设置
textContent来更新内容;替换整个区域的外层 HTML 可能会破坏监听。 - 重复宣读需要内容变化——连续两次写入相同字符串通常不会产生第二次宣读。可添加计数器、时间戳,或短暂清空区域。
- 多步骤更新期间配合
aria-busy="true"以避免宣读不完整的内容。 - 配合
aria-atomic以控制宣读的是差异部分还是整个区域。
常见错误
- 在与内容相同的执行周期内创建实时区域——不会触发宣读。
- 所有情况都使用
aria-live="assertive"。用户会将该标签页静音。 - 在可聚焦控件上设置
aria-live。实时区域用于状态更新,而非交互控件。 - 用
display: none隐藏实时区域。CSS 隐藏的区域同样从无障碍树中隐藏,不会产生任何宣读;应使用视觉隐藏技术(clip / sr-only)代替。 - 一次性将大量内容(整段文字)塞入实时区域——用户无法略读。
- 消息被读取后忘记清空区域,导致后续相同内容的更新不产生任何宣读。
示例
<form>
<label for="zip">邮政编码</label>
<input id="zip" name="zip" />
<button type="submit">查询</button>
</form>
<!-- 从初始渲染起始终存在 -->
<div id="lookup-status" aria-live="polite" class="sr-only"></div>
<script>
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault();
const status = document.getElementById('lookup-status');
status.textContent = '正在查询位置……';
const place = await lookup(document.getElementById('zip').value);
status.textContent = `位置:${place}`;
});
</script>