ARIA 属性不是无障碍开关,而是原生语义不足时对辅助技术的必要补充;必须在原生标签无法准确传达行为或状态时使用,且需与 JavaScript 全量同步行为与状态,否则比不加更误导用户。

aria-* 属性不是“让网页变无障碍”的开关,而是当 HTML 原生语义表达不了动态状态或关系时,给辅助技术补一句“人话”——它只在必要处起效,加错地方反而破坏体验。
aria-\* 什么时候必须加?
只有原生标签无法准确传达行为或状态时才用。比如:
-
<div>模拟下拉菜单 → 必须加role="listbox"+aria-expanded+aria-activedescendant - 纯
<svg>图标按钮 → 必须加aria-label或aria-labelledby,否则读屏器跳过不读 - 无限滚动容器插入新条目 → 必须加
aria-live="polite",否则用户听不到新增内容 - 模态框用
<div class="modal">实现 → 必须同时设role="dialog"、aria-modal="true"、aria-labelledby
别给 <button> 加 aria-disabled="true" 还手动控制样式——它原生就支持 disabled 属性,重复设置易导致状态不同步。
常见误用:aria-hidden="true" 的三大陷阱
aria-hidden="true" 不是“隐藏元素”,只是告诉读屏器“别管这个节点及其所有子节点”。它和视觉表现完全解耦:
立即学习“前端免费学习笔记(深入)”;
使用 PptxGenJS 和 Python 解析,将结构化的 HTML/CSS 幻灯片内容转换为原生、可编辑的 PowerPoint(.pptx)演示文稿。
- 对整个导航栏父容器加
aria-hidden="true",再用 JS 切换显示/隐藏 → 子项按钮永远无法被聚焦,键盘用户卡死 - 在模态框打开时给
<body>加aria-hidden="true"→ 背景内容被跳过,但焦点没锁住,Tab 键仍能切到不可见区域 - 父元素设了
aria-hidden="true",子元素再写aria-hidden="false"→ 完全无效,继承强制隐藏
真正需要“视觉隐藏但读屏可读”时,用 .sr-only 类(靠 position: absolute; clip: rect() 移出视口),而不是 aria-hidden。
role 和 aria-\* 必须协同生效
单独写 role="switch" 没用,必须配齐三要素:
-
aria-checked="true"或"false"表示当前状态 -
tabindex="0"让它可获焦 - 监听
Space键并更新aria-checked值,同时触发视觉反馈
同理,role="tablist" 要搭配每个 role="tab" 的 aria-selected 和 aria-controls,否则读屏器只知“这是个 tab 列表”,不知哪个激活、连向哪块面板。
验证是否真有效?别信 DevTools 的 Accessibility 面板
它只检查属性是否存在,不校验逻辑是否闭环。真实验证必须:
- 用 NVDA(Windows)或 VoiceOver(macOS)完整走一遍交互流程:打开/关闭、Tab 导航、状态切换
- 检查
aria-live区域是否真被朗读——注意 DOM 更新方式:innerHTML替换会丢失绑定,推荐用textContent或appendChild - 确认读屏器当前模式(浏览/虚拟光标)是否影响
aria-live="polite"的触发时机
最常被忽略的点:ARIA 不是贴纸,它是契约——你声明了 role 和 aria-*,就得用 JS 全量实现对应行为与状态同步,否则比不加更误导用户。


















