<p>应避免滥用 role="application",需采用语义化 ARIA 角色(如 combobox、listbox)、正确设置 aria-* 属性、确保焦点元素兼具 tabindex/role/状态属性,并严格遵循 WAI-ARIA 键盘导航规范。</p>

role="application" 会破坏屏幕阅读器默认交互
很多开发者看到“复合控件”就下意识套 role="application",以为这样能接管键盘行为——结果反而让屏幕阅读器退出表单模式,Tab 键失效、Enter 不触发、甚至 Arrow 键完全失灵。这不是增强可访问性,是主动屏蔽辅助技术。
真正该做的是:保留标准语义结构,用 role="combobox"、role="listbox"、role="tree" 等具体 widget 角色,再配合 aria-* 属性补足状态和关系:
-
aria-expanded控制展开/收起状态(别只靠 class 切换) -
aria-activedescendant指向当前焦点项(替代tabindex="-1"+ 手动 focus) -
aria-owns显式声明逻辑父子关系(当 DOM 结构不嵌套时必需) - 所有可交互子项必须有
role(如role="option"、role="treeitem"),不能只靠tabindex
focusable 元素必须同时满足 tabindex + role + aria-*
只加 tabindex="0" 或只设 role="button" 都不够。屏幕阅读器只认“可聚焦 + 有语义角色 + 状态可感知”的组合。常见翻车点:
- 下拉菜单的 trigger 按钮写了
tabindex="0"却漏了role="button"→ NVDA 当成普通文本 - 列表项用了
role="option"但没加aria-selected="true/false"→ VoiceOver 不读选中态 - 自定义滑块拖拽区设了
tabindex="0"和role="slider",却没配aria-valuenow、aria-valuemin、aria-valuemax→ JAWS 直接跳过
DOM 结构不等于 ARIA 结构,aria-owns 是救命绳
React/Vue 里常见 portal 渲染弹层(比如 Popover 插在 body 底下),导致视觉上是下拉菜单,DOM 上却是兄弟节点。这时 aria-owns 不是可选项,是必填项:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
立即学习“前端免费学习笔记(深入)”;
<div role="combobox" aria-owns="dropdown-list"> <input aria-controls="dropdown-list" /> </div> <div id="dropdown-list" role="listbox">...</div>
注意:aria-owns 的值必须是真实存在的 ID;如果弹层是动态创建的,得等它挂载完再设置属性,否则屏幕阅读器读不到。
- 不要用
aria-owns模拟嵌套来绕开语义错误(比如把按钮“拥有”一个 div 来假装是菜单) -
aria-owns优先级高于 DOM 层级,但会覆盖原有父子关系,慎用于复杂嵌套 - 某些旧版 Safari + VoiceOver 组合对
aria-owns支持不稳定,关键操作仍需 fallback 键盘逻辑
键盘导航不是“加个 onkeydown 就完事”
复合控件的键盘行为必须严格对标 WAI-ARIA Authoring Practices 指南,比如 role="listbox" 必须支持 ArrowDown/ArrowUp 移动焦点、Home/End 跳首尾、PageDown/PageUp 分页——这些不是锦上添花,是屏幕阅读器用户的默认预期。
- 别用
event.preventDefault()拦截所有键,至少放过Tab、Shift+Tab、Esc -
role="tree"必须响应Right/Left键展开/收起,不能只靠鼠标点击 - 搜索框内按
Enter提交?先确认是否在role="combobox"场景下——此时Enter应该关闭下拉并选中高亮项,而非直接提交
最常被忽略的一点:键盘焦点样式必须肉眼可见。很多团队用 outline: none 一刀切,又没补自定义 focus ring,等于把键盘用户直接关在门外。


















