大型门户网站使用WAI-ARIA的核心原则是“精准补缺而非堆砌”,必须严格遵循原生语义优先、role仅在无法用<nav>等标签时才补充、aria-live需按场景选polite/assertive、表单错误须同步aria-invalid与aria-describedby、自定义组件必须手写完整键盘行为,否则将导致导航混乱、状态误报或功能不可用。

大型门户网站用 WAI-ARIA 不是“加不加”的问题,而是“怎么加才不翻车”的问题。盲目堆 role、aria-* 属性反而会破坏屏幕阅读器对原生语义的正确解析,导致导航混乱、状态误报、焦点丢失——这类问题在日均 PV 千万级的门户首页上,比功能 bug 更难定位。
为什么不能直接给 <div> 加 role="navigation"
因为 <nav> 已自带 role="navigation",重复声明不仅冗余,还可能触发某些旧版辅助技术的兼容性降级逻辑(如 IE11 + JAWS 2019 组合下会跳过该区域);更关键的是,<nav> 还隐式提供键盘导航支持(Tab 进入后可按方向键遍历子链接),而 <div role="navigation"> 不会自动获得这些行为,必须手动补全 tabindex、keydown 事件监听和焦点管理。
- 只在无法使用语义化标签时才用 ARIA 角色:比如 React 封装的
<HeaderNav>组件最终渲染为<div class="header-nav">,此时才需显式加role="navigation" - 若已用
<nav>,就别再写role="navigation"—— Lighthouse 会报aria-allowed-role错误 - 所有带
role的容器,必须确保其子元素角色合法:比如role="menu"下只能有role="menuitem"或role="menuitemcheckbox",不能塞<a>或<button>
aria-live 在新闻流/广告位动态更新时的坑
门户首页常有“热点新闻轮播”或“实时广告位切换”,靠 JS 频繁替换 DOM。若只用 innerHTML = newHtml,屏幕阅读器根本感知不到变化。用 aria-live 是对的,但选错 polite 还是 assertive 会导致两种极端:
-
aria-live="polite":适合新闻流更新,但若连续快速插入 3 条,部分读屏器(如 NVDA 2023.1)会合并播报成一句,丢失时间顺序 -
aria-live="assertive":适合弹窗错误提示,但用在广告位上会强行中断用户当前朗读,引发投诉 - 真正稳妥的做法是:用
aria-live="polite"+ 每次更新前清空容器(el.textContent = ""),再 append 新节点,避免 DOM diff 导致的 aria-live 失效
表单错误提示必须绑定 aria-invalid 和 aria-describedby
门户注册页常含手机号、验证码、协议勾选等字段,后端校验失败后仅在输入框下方显示红色文字提示,对屏幕阅读器用户等于不存在。必须同步操作三处:
立即学习“前端免费学习笔记(深入)”;
- 设置
aria-invalid="true"在输入框上,让读屏器第一时间识别“此字段出错” - 生成唯一 ID 的错误提示元素(如
<div id="phone-error">手机号格式不正确</div>) - 在输入框上写
aria-describedby="phone-error",确保错误文本被连贯朗读 - 切忌用
aria-label覆盖原有提示——它会完全取代 label 内容,导致用户不知道这是“手机号”字段的错误
复杂组件如“城市选择器”必须手写键盘行为
门户天气栏或本地服务入口常用自定义城市选择器(非原生 <select>),即使加了 role="combobox" 和 aria-expanded,若没实现以下键盘逻辑,仍会被 WCAG 2.1 AA 直接判为不合规:
- Down Arrow / Up Arrow:展开面板后,聚焦第一个/最后一个选项项
- Escape:收起面板并恢复输入框焦点
- Enter / Space:选中当前高亮项并关闭面板
- 字符键(如 “上”):跳转到首个以该字开头的选项(不是简单 filter)
这些行为无法靠 ARIA 属性自动获得,必须用 JS 显式监听并控制焦点流。漏掉任意一条,视障用户就卡在面板里出不来——这不是体验差,是功能不可用。



















