先明确一个核心观点:ARIA 并非语义化 HTML 的替代方案,而是在原生 HTML 表达能力不足时,用于补充增强的辅助工具。正确使用时,它能够有效解决动态状态更新、自定义交互组件以及跨元素关系描述等原生 HTML 难以处理的场景;然而一旦使用不当,反而会导致屏幕阅读器完全无法识别内容,甚至连基础的信息传递都会失效。
什么时候必须用 aria-live="polite"
aria-live="polite" 属性的核心作用是向屏幕阅读器广播动态内容更新。当页面内容通过 JavaScript 异步加载——例如 AJAX 请求加载列表数据、表单提交后的状态反馈、实时推送通知等——且需要主动告知用户内容发生变化时,这个属性就派上了用场。它不会改变 DOM 结构,而是专门向辅助技术发送内容更新信号。
以下几个关键点值得特别留意:
- 避免使用
aria-hidden="true"来控制区域显示与隐藏,因为被隐藏的内容屏幕阅读器完全无法感知,相当于做了无用功。 - 推荐的做法是使用
作为独立的动态内容容器,通过 JavaScript 执行appendChild操作或直接赋值textContent属性,而非整体替换 innerHTML。 - 谨慎使用
aria-live="assertive"属性,除非是错误弹窗或需要立即中断用户当前操作的紧急提示;否则它会强行打断屏幕阅读器的当前朗读,给用户造成不必要的困扰。 - 兼容性方面需要特别注意:Internet Explorer 不支持
aria-live属性,这种情况下需要降级处理,通过强制移动焦点来实现,例如使用el.focus()配合tabindex="-1"属性。
自定义下拉菜单必须配齐 role + aria-expanded + aria-activedescendant
许多开发者误以为只需为下拉菜单添加 role="listbox" 属性即可满足无障碍要求,但实际效果是:浏览器会提示“缺少必需子角色”,屏幕阅读器则会直接跳过整个组件区域,导致所有努力付诸东流。
正确的实现方式如下:
role="listbox"容器内的所有选项元素必须使用role="option"角色,不能直接用或替代。- 触发按钮需要包含
aria-haspopup="listbox"和aria-expanded="false"属性,当 JavaScript 打开或关闭下拉菜单时,必须同步更新aria-expanded的值。 - 焦点管理不能依赖
tabindex="0"粗暴推进,那样只会让交互逻辑更加混乱。正确的做法是使用aria-activedescendant属性指向当前高亮选项的 ID,并通过监听ArrowUp/ArrowDown键来手动切换焦点。 - 如果使用
innerHTML =直接替换下拉菜单内容,aria-activedescendant的指向关系会断裂。必须改用appendChild或insertAdjacentElement方法来维护引用。
模态框缺一不可:role="dialog" + aria-modal="true" + aria-labelledby
这三个属性缺一不可,否则屏幕阅读器很可能将模态框视为普通浮动层而忽略。其中 aria-modal="true" 尤为关键,它是实现背景内容隔离的核心属性。
具体来说:
role="dialog"仅声明模态框类型,不会阻止背景内容的朗读;aria-modal="true"才能真正切断可访问树中的背景节点。旧版 IE 不支持此属性,需要降级使用inert属性或焦点锁定方案。aria-labelledby属性必须指向一个实际存在、已渲染完成、具有语义化的可见标题元素,例如。不能指向空的确认删除
元素,也不能引用 JavaScript 生成但尚未插入 DOM 的节点。- 打开模态框时,必须通过
focus()方法主动将焦点移至第一个可聚焦元素,通常是取消按钮或标题元素;否则键盘用户将无法从背景内容中跳出。 - 切勿对
元素添加aria-hidden="true"属性——这会导致整个页面从可访问树中移除,用户将完全无法感知任何内容。正确的做法是给背景容器添加inert属性或tabindex="-1",并配合焦点捕获机制。
