aria属性不能自动提升可访问性,仅在原生语义不足时有效;用错比不用更糟,需严格遵循role、aria-*、焦点管理及dom操作规范。

ARIA 属性不能“自动提升”可访问性,它只在原生 HTML 语义无法表达组件行为或状态时才有效;用错比不用更糟,尤其在自定义下拉、标签页、模态框这类复杂交互组件中。
自定义下拉菜单必须配齐 role + aria-expanded + aria-activedescendant
仅用 role="listbox" 不够,浏览器和读屏器会报“缺少必需子角色”或直接忽略。它需要完整语义链:
-
role="listbox"必须包裹role="option"(不能是div或li) - 触发按钮要加
aria-haspopup="listbox"和aria-expanded="false",且 JS 必须同步更新该值 - 焦点管理不能靠
tabindex暴力推进——得用aria-activedescendant指向当前高亮项的 ID,并监听 ArrowUp/Down 键手动切换 - 若用
innerHTML = ...替换整个下拉内容,aria-activedescendant会失效,必须用appendChild或insertAdjacentElement
模态框需同时满足 role="dialog" + aria-modal="true" + aria-labelledby
三者缺一不可,少一个就可能被读屏器当作普通浮层跳过:
-
role="dialog"告诉辅助技术“这是对话框”,但不阻止背景朗读;aria-modal="true"才真正隔离背景内容(旧版 IE 不支持,需 fallback 到inert或焦点锁定) -
aria-labelledby必须指向一个**真实存在的、可见的、有语义的标题元素**(如<h2 id="modal-title">确认删除</h2>),不能是空span或 JS 动态生成后未插入 DOM 的节点 - 打开模态框时,必须用
focus()主动将焦点移到首个可聚焦元素(通常是取消按钮或标题),否则键盘用户卡在背景 - 禁止对
加aria-hidden="true"——这会让整个页面从可访问树中消失,正确做法是给背景容器加inert或用tabindex="-1"+ 焦点捕获
标签页(tablist)必须用 aria-selected + aria-controls + role 链联动
光写 role="tablist" 和 role="tab" 只是画了个架子,状态不同步会导致读屏器读“未选中”但视觉已高亮:
- 每个
role="tab"必须带aria-selected="false"(初始状态),JS 切换时同步改值并触发click或keydown事件 -
aria-controls要精准指向对应role="tabpanel"的 ID,不能指向父容器或错误 ID -
role="tabpanel"必须有aria-labelledby指回其所属 tab 的 ID,形成双向关联 - Tab 键导航应只在 tabs 之间循环(
roving tabindex),不能误入 panel 内容;用Shift+Tab进入 panel 后,焦点应落在第一个可聚焦子元素上
aria-live="polite" 不是“加了就能通知”,它依赖 DOM 插入方式和位置
动态加载内容(如搜索建议、表单错误提示、无限滚动新条目)若只靠 JS 改变 innerHTML,读屏器大概率静默——因为 DOM 替换会中断可访问树引用:
-
aria-live="polite"容器必须是**静态存在**的,不能每次请求都重建;最好放在 DOM 顶部或固定位置(如<main></main>外) - 新内容必须用
appendChild、insertAdjacentHTML("beforeend", ...)或append()追加,禁用innerHTML = ... - 若内容含交互元素(如建议项里的按钮),需确保它们在插入后能被 Tab 访问——
aria-live不自动赋予焦点,得手动focus() - 不要在
aria-live区域里放aria-hidden="true"子元素,它会继承隐藏,导致通知内容被跳过
最容易被忽略的是:ARIA 属性本身不处理键盘逻辑、不控制焦点流、不替代语义结构。它只是“告诉辅助技术发生了什么”,而“发生什么”这件事,得靠你写的 JS 和 HTML 结构来真正实现。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











