aria是原生语义失效时的精准语义注射器,非补丁;必须分清role(定义“是什么”)、aria-*属性(补充“有何特征”)、state(表达“当前状态”)三类,混用或误加将导致语义与行为脱钩。

ARIA不是语义补丁,而是原生语义失效时的精准语义注射器——加错地方比不加更危险。
role、aria-* 属性、state 三类必须分清
ARIA 实际就三类东西:角色(role)定义“这是什么”,属性(如 aria-label、aria-describedby)补充“它有什么特征”,状态(如 aria-expanded、aria-checked)表达“它现在是什么样”。它们不能混用:给一个 div 加 role="button" 后,若不手动加 tabindex="0" 和键盘事件监听,它只是个“名义上的按钮”,实际无法聚焦、无法空格/回车触发;若只设 aria-expanded="true" 却没同步控制菜单显隐,读屏器会说“已展开”,但用户看不到内容——语义和行为彻底脱钩。
哪些地方绝对不该加 role 或 aria-*?
以下操作看似“加强语义”,实则主动破坏无障碍:
- 给
<button></button>加role="link"或role="switch"—— 浏览器已内置完整语义和状态同步,覆盖后读屏器可能误报“链接”却无法跳转,或把开关状态读成按钮 - 给
<input type="checkbox">手动写aria-checked="true"—— 它自己会根据 checked 属性自动更新,重复设置极易导致 JS 更新了 DOM 但没同步 aria-checked,状态错乱 - 给
<table> 的 <code><tr> 加 <code>role="search"—— 直接触发 ARIA 规范的aria-requires-children错误,表格结构语义直接断裂 - 对整个
或导航栏父容器加aria-hidden="true"来“隐藏背景”—— 键盘焦点仍可进入子元素,但读屏器已跳过,用户卡在不可见又不可读的死区 - 用
innerHTML = newContent替换整个区域 → 原节点被销毁,监听上下文丢失,读屏器完全无感 - 仅修改
textContent但未触发 DOM 插入事件 → 大部分读屏器(尤其 VoiceOver 浏览模式下)静默忽略 - 把
aria-live加在<button></button>上 → 它只对容器内新增子节点有效,按钮自身状态变化该用aria-pressed或aria-expanded -
role="dialog"—— 告诉读屏器“这是对话框” -
aria-modal="true"—— 指示背景应被屏蔽(现代浏览器据此禁用背景焦点) -
aria-labelledby="id-of-title"—— 提供可朗读的标题,否则读屏器只说“对话框”,不说“登录对话框” - 手动焦点管理 —— 打开时
focus()到第一个可焦元素(通常是标题或首输入框),关闭后恢复到触发按钮
aria-live 不是“一加就响”,得看怎么更新 DOM
aria-live="polite" 只在特定条件下被朗读:它监听的是“向容器内动态插入新节点”的行为,而不是“内容变了”这个结果。常见失效场景:
正确做法是用 appendChild() 或 insertAdjacentElement() 插入新元素,且确保容器本身不被重建。
dialog 必须配齐四要素,缺一不可
只写 role="dialog" 是无效的。真实模态框需同时满足:
漏掉任意一项,键盘用户就可能 Tab 出模态框、读屏器无法定位上下文、或焦点永远卡在看不见的地方。
最常被忽略的其实是「状态同步」:ARIA 属性没有魔法,它只是把 JS 控制的逻辑翻译成辅助技术能听懂的话。你改了视觉状态,就必须同步改 aria- 值;你插入了新 DOM,就得确保它进的是 aria-live 容器而不是被整个替换掉。否则,无障碍就变成了障眼法。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











