仅加aria role不够,必须补全命名、状态同步和键盘行为:role="navigation"需aria-label区分用途,aria-live需append而非innerhtml替换,role="dialog"须配aria-modal与焦点管理,下拉菜单需同步aria-expanded和aria-activedescendant。

老旧 HTML 结构(比如一堆 <div> 套 <code><div>)本身没有语义,屏幕阅读器只能读“区域”“段落”“图形”,无法识别导航、主内容、按钮等意图——<strong>仅加 ARIA role 不够,必须补全可访问性链路:命名 + 状态同步 + 键盘行为</strong>。
<h3>role="navigation" 为什么加了还是被跳过?</h3>
<p>只写 <code><div role="navigation">,读屏器会识别为“navigation”,但用户按 NVDA 的 D 键跳界标时可能卡住或直接跳过。原因不是没加 role,而是:<br>• 缺少唯一标识:多个 <code>role="navigation" 共存时,必须用 aria-label 或 aria-labelledby 区分用途,例如 aria-label="主导航" 或 aria-labelledby="nav-main-title"
• 子元素角色不合法:若内部混用 <button></button> 或 <span></span> 而非 role="link",部分辅助技术(如 JAWS + IE11)会降级处理甚至忽略整个区域
• 没补键盘遍历逻辑:原生 <nav></nav> 支持 Tab 进入后方向键移动,而 div + role 不会自动获得该能力,需手动监听 keydown 并管理焦点顺序
aria-live="polite" 在旧项目轮播中为何静默失效?
老旧门户首页常用 innerHTML = newHtml 替换新闻轮播内容,即使加了 aria-live="polite",NVDA 或 VoiceOver 也毫无反应。根本原因是:
• DOM 节点被完全销毁重建,aria-live 监听上下文丢失
• 读屏器只响应“向容器内插入新节点”,而非“文本内容变化”
• VoiceOver 在浏览模式下会忽略 aria-live="polite",除非用户已聚焦在该区域
建议做法:
• 清空容器:el.textContent = ""
• 动态插入新节点:el.appendChild(newItem) 或 el.insertAdjacentElement("beforeend", newItem)
• 避免对整个轮播容器设 aria-hidden="true" 后再用 CSS 显示——读屏器已跳过,视觉显示毫无意义
给 <div class="modal"> 加 role="dialog" 还不够
<p>很多旧系统模态框只加了 <code>role="dialog",结果屏幕阅读器读作“对话框”,但焦点仍能落到背景、关闭按钮不可键盘触发、标题不自动朗读。真实可用的模态框必须同时满足:
• role="dialog" —— 定义类型
• aria-modal="true" —— 告知辅助技术应屏蔽背景(现代浏览器据此禁用背景焦点)
• aria-labelledby="modal-title" —— 指向可见标题元素(ID 必须存在且唯一)
• 手动实现焦点锁定:打开时 el.querySelector("[autofocus]")?.focus() 或聚焦标题;关闭时恢复上一个焦点元素
• 不要用 aria-hidden="true" 给 ,这会让整个页面从无障碍树中消失,正确做法是用 inert 属性(Chrome/Firefox/Edge 已支持),或退化方案:遍历所有可聚焦元素设 tabindex="-1" 并移除 tabindex="0"
自定义下拉菜单的 aria-expanded 和 aria-activedescendant 怎么同步?
旧项目里常见 <div class="dropdown"> 实现的下拉,光靠 JS 控制显隐远远不够。读屏器需要知道“是否展开”和“当前高亮哪一项”:<br>• <code>aria-expanded 必须与视觉状态严格一致:菜单展开时设为 "true",收起时设为 "false";不能只靠 CSS display 判断
• aria-activedescendant 只在 role="listbox" 容器上有效,值必须是当前高亮 <div role="option"> 的 ID,例如 <code>aria-activedescendant="opt-3"
• 当用户用方向键移动高亮项时,要实时更新该属性,且确保对应 option 元素有 id
• 不要给触发按钮加 role="listbox",它只是开关,应设 aria-haspopup="listbox" 和 aria-expanded,并用 aria-controls="listbox-id" 关联目标容器
最常被忽略的不是“加不加 ARIA”,而是“加完之后 JS 有没有真正接管对应行为”。role 是声明,不是魔法——它把语义交给了辅助技术,但交互逻辑、状态同步、焦点流,全得自己手写。旧项目改造时,优先检查每个带 role 的元素是否具备:可聚焦、可键盘操作、状态可视、名称可读。缺一不可。











