role="tooltip" 仅是语义标记,不自动触发读屏播报;正确做法是用 aria-describedby 关联 tooltip 元素,确保其在 DOM 中静态存在、有唯一 id、设 role="tooltip" 和 aria-hidden="true",并通过 CSS 隐藏而非移除。

role="tooltip" 本身不会触发读屏播报
这是最常见的误解:role="tooltip" 只是语义标记,告诉辅助技术“这个元素是提示框”,但**不自动绑定悬停行为或语音播报**。读屏软件(如 NVDA、VoiceOver)只在焦点进入、键盘导航到该元素,或它被 aria-describedby 关联时才读出内容——鼠标悬停默认不触发任何可访问行为。
正确做法:用 aria-describedby 关联 tooltip 内容
让读屏播报 tooltip 文本,关键不是悬停,而是建立可访问的“描述关系”。典型场景是:一个按钮旁有个隐藏的 tooltip 元素,你想让读屏在聚焦按钮时就念出提示。
- tooltip 元素需有唯一
id,且设role="tooltip"和aria-hidden="true"(避免被意外聚焦) - 触发元素(如按钮)通过
aria-describedby="tooltip-id"显式引用它 - 不要依赖
display: none或visibility: hidden隐藏 tooltip——它们会切断读屏访问;改用visibility: hidden; position: absolute; clip: rect(0 0 0 0)或clip-path: inset(100%)
示例:
<button aria-describedby="save-tip">保存</button> <div id="save-tip" role="tooltip" aria-hidden="true">点击后保存所有未提交的更改</div>
鼠标悬停 ≠ 可访问交互,别试图“模拟”它
很多开发者想“让鼠标悬停时读屏也播报”,但这是反模式。原因很实际:
立即学习“前端免费学习笔记(深入)”;
- 屏幕阅读器用户不使用鼠标,也不感知悬停状态
- 部分读屏(如 JAWS)在鼠标移动时会短暂朗读目标元素,但这不可靠、不可控,且不是标准行为
- 强行用
onmouseenter+focus()强行聚焦 tooltip 会导致焦点跳跃、破坏键盘流,反而降低可用性
真正要做的,是确保 tooltip 内容对键盘和读屏用户始终可发现、可理解——比如把提示文字直接放进按钮的 aria-label,或确保 tooltip 元素在 DOM 中紧邻触发控件且被正确关联。
移动端和 VoiceOver 的特殊处理
iOS 上 VoiceOver 默认不响应悬停(无鼠标),但会读取 aria-describedby 关联的内容——前提是 tooltip 元素在初始渲染时就在 DOM 中(不能纯 JS 动态插入后才加 id)。常见坑:
- 动态创建 tooltip 后忘记调用
element.setAttribute('id', '...'),导致aria-describedby指向失效 - 用
aria-live="polite"包裹 tooltip 内容试图“推送给读屏”——这会让提示重复播报,且与 tooltip 语义冲突 - 给 tooltip 加
tabindex="-1"并试图手动.focus():既没必要,又可能打断用户当前焦点
最稳的路径:静态声明 id,用 aria-describedby 关联,用 CSS 隐藏而非移除,保持 DOM 稳定性。



















