只有当组件持续自动更新时间且变化对用户关键时才用role="timer",必须配合aria-live="polite",更新完整可读时间字符串,避免innerhtml覆盖,并在ios加tabindex="-1"提升voiceover兼容性。

role="timer" 什么时候该用
只有当组件在持续、自动更新时间(比如每秒减1),且这个变化对用户操作或流程有关键影响时,才用 role="timer。不是所有带数字变动的组件都适合——比如只是点击后跳转前显示“3…2…1”,没实时刷新,就不该加。
常见误用场景:role="timer" 被塞进一个靠 setTimeout 每秒手动重绘但没声明 aria-live 的倒计时;或者用在静态文案里,比如“剩余时间:60秒”只渲染一次。这两种情况读屏器根本不会报出变化。
必须配合 aria-live="polite" 才有效
role="timer" 本身不触发读屏播报,它只是语义标注;真正让屏幕阅读器感知更新的是 aria-live。漏掉这个,等于挂了标牌却没装喇叭。
- 用
aria-live="polite"(推荐):不打断当前语音,等读屏器空闲时播报新时间 - 避免
aria-live="assertive":倒计时一般不需要插话式强播报,容易干扰用户操作 - 确保更新内容是完整可读的时间字符串,比如“还剩 27 秒”,而不是只更新数字“27”
更新 DOM 时别破坏无障碍树
每次更新倒计时数值,如果用 innerHTML 或 textContent 直接覆盖整个容器,可能触发读屏器重读整块区域,导致重复或跳读。更稳妥的做法是只更新文本节点,或用 aria-label 动态同步。
示例(推荐):
<div role="timer" aria-live="polite" id="countdown">还剩 30 秒</div>
JS 更新时:
document.getElementById('countdown').textContent = `还剩 ${seconds} 秒`;
别写成:el.innerHTML = <span>${seconds}</span>——嵌套标签会干扰 aria-live 的文本提取逻辑。
注意 Safari + VoiceOver 的兼容细节
iOS/macOS 上 VoiceOver 对 role="timer" 支持较弱,有时忽略 aria-live 更新。实测发现:若容器有 tabindex="-1",能显著提升播报稳定性。
安全写法:
<div role="timer" aria-live="polite" tabindex="-1" id="countdown">还剩 15 秒</div>
另外,iOS Safari 不支持 aria-live 在非焦点元素上可靠触发,所以倒计时区域最好保持可聚焦(哪怕只是临时 tabindex="-1"),否则用户切换到其他页面再切回来,可能错过中间更新。
真正难的不是加属性,而是让每次更新都被准确、及时、不打扰地传达出去——尤其当用户正用键盘操作表单,而倒计时在角落悄悄归零时。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











