不推荐用 aria-haspopup="listbox" 标记自定义下拉选框,因主流读屏软件支持极弱;应改用 role="combobox" 或 role="button" 配 aria-expanded、role="listbox" 容器、aria-controls 关联及完整键盘交互逻辑。

aria-haspopup="listbox" 的适用场景是否准确
不推荐用 aria-haspopup="listbox" 标记自定义下拉选框。WAI-ARIA 规范中,aria-haspopup 的合法值是 "true"、"false"、"menu"、"listbox"、"tree"、"grid"、"dialog",但关键不在“能不能写”,而在“屏幕阅读器是否按预期理解”。目前主流读屏软件(NVDA、VoiceOver、JAWS)对 aria-haspopup="listbox" 的支持极弱——多数仍将其当作普通按钮处理,不会主动提示“可展开列表框”,也不触发 listbox 导航模式。
自定义下拉该用什么 ARIA 组合
真正起作用的是角色 + 状态 + 关联关系的组合,而不是单靠 aria-haspopup。核心要点:
- 触发按钮用
role="combobox"或role="button",并配aria-expanded="true/false" - 下拉容器必须明确声明
role="listbox",且每个选项用role="option" - 用
aria-controls指向 listbox 的 ID(例如aria-controls="my-listbox"),比aria-haspopup更可靠 - 若支持键盘搜索(如输入首字母跳转),需加
aria-autocomplete="list"和aria-haspopup="listbox"仅作辅助,不能省略其他属性
常见错误:只写 aria-haspopup 就以为无障碍达标
这是最典型的“伪无障碍”陷阱。现象包括:
- 读屏朗读按钮时只说“按钮”,不提“下拉”或“可展开”
- 按下
↓键后焦点未进入 listbox,或无法用方向键遍历选项 - 选中项没有
aria-selected="true",导致用户无法确认当前选择 - listbox 容器缺少
aria-labelledby或aria-label,读屏无法告知列表用途
这些都和是否写了 aria-haspopup="listbox" 无关,而取决于整体角色结构与状态同步是否到位。
立即学习“前端免费学习笔记(深入)”;
React/Vue 中动态更新的注意事项
框架里容易忽略 DOM 层面的同步时机:
-
aria-expanded必须在下拉面板实际显示/隐藏后立即更新,不能依赖动画结束回调(读屏不等 CSS 动画) - listbox 插入 DOM 后,若用
focus()聚焦第一个option,需确保该元素已渲染且tabindex="-1"已设置 - 避免在 listbox 上直接写
display: none—— 应用aria-hidden="true"配合visibility: hidden或opacity: 0,否则读屏可能完全忽略该容器
真正决定体验的,是触发器、容器、选项三者之间的角色链和状态一致性,不是某个属性值写得有多“标准”。



















