绝大多数动态提示用 aria-live="polite" 就够了,如“已保存”“搜索完成”,它等待当前语音结束后播报,不打断用户;仅“登录失败”“余额不足”等需立即干预的紧急场景才用 aria-live="assertive",但需注意兼容性与防抖限制。

aria-live="polite" 和 aria-live="assertive" 到底该选哪个
绝大多数动态提示用 aria-live="polite" 就够了,比如“邮箱格式正确”“已保存”“搜索完成”。它会让屏幕阅读器等当前语音自然结束后再播报,不打断用户操作。只有真正需要立即抢话的场景(如“登录失败”“余额不足”)才考虑 aria-live="assertive",但要注意:NVDA + Firefox 组合下 aria-live="assertive" 容易被跳过或重复读,不是写了就一定打断。
别滥用 aria-live="assertive"——连续两次快速更新,第二次大概率静默;写成 aria-live=" assertive "(前后带空格)或拼错,也会失效。
aria-live 区域必须提前写死在 HTML 里
不能靠 JavaScript 动态创建并加 aria-live 属性,屏幕阅读器只在元素首次挂载时解析该属性。下面这种写法完全无效:
const el = document.createElement('div');
el.setAttribute('aria-live', 'polite');
document.body.appendChild(el);
正确做法是初始 HTML 就声明好容器:
立即学习“前端免费学习笔记(深入)”;
<div id="status" aria-live="polite" aria-atomic="true" aria-relevant="text" class="sr-only"></div>
class="sr-only" 对应的 CSS 要用 clip 或 position: absolute 隐藏,不能用 display: none 或 visibility: hidden,否则区域直接不可见于辅助技术树(AX Tree)。
更新内容必须用 textContent,且注意清空和节奏
直接改 textContent 是最稳妥的触发方式;用 innerHTML 插入带标签的内容容易被跳过或读错,还可能引入 XSS 风险。
常见错误包括:
- 没清空上一次内容,导致新旧文本堆叠,读出来像“密码强度:弱密码强度:强”
- 连续快速赋值:
el.textContent = "错误"; el.textContent = "请重试";—— 第二句大概率丢失 - 在父级有
aria-hidden="true"的弹窗里放 live 区域,整个区域被屏蔽
推荐写法:
const liveEl = document.getElementById('status');
liveEl.textContent = ''; // 先清空
setTimeout(() => {
liveEl.textContent = '验证码已发送';
}, 50); // 微小延迟防抖
为什么加了 aria-live 还是不读?排查三步走
第一看是否在 AX Tree 里:打开 Chrome DevTools → Elements → 右键元素 → “Inspect Accessibility”,确认该节点存在且 aria-live 值为 polite 或 assertive;
第二查 DOM 更新方式:用 insertAdjacentHTML("beforeend", ...) 追加内容,aria-live="assertive" 基本不响应;
第三验上下文:父级有没有 aria-hidden="true"、容器是否被 CSS 动画干扰、是否用了 v-if 或 key 导致组件销毁重挂——这些都会让 live 区域“断连”。
最常被忽略的是:live 区域得是静态存在的,且更新必须伴随真实 DOM 变更;JS 变量变了,但没写进 DOM,屏幕阅读器根本不知道。



















