ARIA 属性仅在原生语义不足时使用,必须严格对应三类场景:自定义控件、动态内容更新、状态实时同步;滥用(如为 button 加 aria-label、header 加 role="banner")反而破坏可访问性。

ARIA 属性不是“加了就可访问”,它只在原生语义无法表达组件行为或状态时才该出现;用错比不用更糟,尤其在自定义下拉、模态框、标签页这类组件里。
哪些地方必须加 ARIA,哪些加了反而破坏可访问性
真正需要 ARIA 的只有三类场景:自定义控件(如 div 实现的下拉)、动态内容更新(如 AJAX 加载列表)、状态实时同步(如折叠面板展开态)。其余多数情况属于画蛇添足:
-
<button>提交</button>再加aria-label="提交表单":覆盖原生文本,导致重复播报或只读 label -
<header>加role="banner":HTML5 已隐含该 role,重复声明可能被忽略或触发警告 -
<h2>设置</h2>加aria-label="用户设置页面":标题可访问名称就是自身文本,覆盖后信息丢失 -
<img src="logo.png">加aria-label:应该用alt,aria-label在img上无效或行为不可靠
自定义下拉菜单的 ARIA 必须配齐这四点
只写 role="listbox" 不够,浏览器和读屏器会报“缺少必需子角色”或直接忽略。它需要完整语义链:
-
role="listbox"必须包裹role="option"(不能是div或li) - 触发按钮要加
aria-haspopup="listbox"和aria-expanded="false",且 JS 必须同步更新该值 - 焦点管理不能靠
tabindex暴力推进——得用aria-activedescendant指向当前高亮项的 ID,并监听ArrowUp/ArrowDown键手动切换 - 若用
innerHTML =替换整个下拉内容,aria-activedescendant会失效,必须用appendChild或insertAdjacentElement
模态框打开后读屏器还在读背景?检查这三点
仅加 role="dialog" 不足以让辅助技术正确识别模态上下文。常见错误是给 <body> 加 aria-hidden="true",结果整个页面对屏幕阅读器彻底不可见:
立即学习“前端免费学习笔记(深入)”;
- 必须同时满足:
role="dialog"+aria-modal="true"+aria-labelledby(三者缺一不可) -
aria-labelledby必须指向一个**真实存在的、可见的、有语义的标题元素**(如<h2 id="modal-title">确认删除</h2>),不能是空span或 JS 动态生成后未插入 DOM 的节点 - 打开模态框时,必须用
focus()主动将焦点移到首个可聚焦元素(通常是取消按钮或标题),否则键盘用户卡在背景 - 禁止对
<body>加aria-hidden="true"——这会让整个页面从可访问树中消失;正确做法是给背景容器加inert或用tabindex="-1"+ 焦点捕获
aria-live 区域不朗读?DOM 更新方式错了
aria-live="polite" 不是“加了就生效”,它依赖 DOM 更新方式是否触发辅助技术感知:
- 用
innerHTML = "成功保存"替换整个区域会丢失绑定,读屏器大概率不播报 - 应优先用
textContent = "成功保存"或append()/insertAdjacentElement()等 DOM 插入方式 -
aria-live="polite"适合一般提示(如表单成功),aria-live="assertive"仅限关键中断(如登录失败) - 每次更新前建议先清空旧文本再写入新内容,否则某些读屏器会拼接播报(如“已保存已保存”)
最易被忽略的点:ARIA 不解决语义缺失的根本问题。比如用 <div role="navigation"> 包菜单,却不提供键盘方向键导航逻辑,屏幕阅读器能识别“这是导航区”,但用户仍无法操作。



















