关键在于用正确ARIA属性模拟原生select语义:触发按钮设role="combobox"和aria-haspopup="listbox";下拉容器设role="listbox"及aria-labelledby;选项用role="option"并动态管理aria-selected;配合aria-label/aria-valuetext提供清晰标签与实时反馈。

关键在于用正确的 ARIA 属性模拟原生 select 的语义和行为,让屏幕阅读器识别出“这是一个可展开的控件,当前有选中项,列表是它的选项集合”。
用 role="combobox" 和 aria-haspopup="listbox" 标明控件类型
自定义下拉的触发按钮(比如那个带箭头的输入框或按钮)必须声明语义:
-
role="combobox" 表示这是一个组合输入+下拉选择的控件(比
button或div更准确) - aria-haspopup="listbox" 明确告诉屏幕阅读器:点击/空格/回车后会弹出一个列表型菜单(不是菜单栏 menu 或对话框 dialog)
- 如果支持键盘输入搜索(如带过滤的下拉),还要加 aria-expanded 和 aria-controls(指向列表容器 ID)
为下拉列表容器设置 role="listbox" 和 aria-labelledby
展开后的选项容器不能只是 div 包着一堆 div,要赋予明确角色:
- 外层容器设 role="listbox",这是 ARIA 中表示可选列表的标准角色
- 加上 aria-labelledby,值为触发按钮的 ID,建立视觉控件与列表的语义关联(屏幕阅读器读列表时会带上按钮的标签名)
- 避免用
role="menu"—— 它适用于右键菜单、应用菜单栏,不适用于表单选择场景
每个选项用 role="option",并管理选中状态
列表里的每一项必须是独立的可选单元:
- 每项设 role="option",而非
button或div - 当前选中项加 aria-selected="true",其余为
"false"(注意:不是用 CSS class 模拟,必须是真实属性) - 确保选项可通过键盘导航(↑/↓),焦点能落在每个
role="option"上,并触发aria-selected切换 - 若支持多选,还需加 aria-multiselectable="true" 并配合
aria-selected
提供可访问的标签和实时反馈
屏幕阅读器需要知道“这是什么”以及“现在选了什么”:
- 触发按钮本身要有清晰的可访问名称:可用 aria-label(如
aria-label="请选择城市"),或用 aria-labelledby 关联外部label元素 - 当用户用键盘或鼠标选中某一项后,及时更新触发按钮的文本内容,并同步更新 aria-valuetext(例如
aria-valuetext="已选择:北京市"),让屏幕阅读器在焦点离开前读出最新值 - 避免仅靠颜色或图标传达状态(如仅用勾选图标表示选中),必须有文字或 ARIA 层面的明确表达
不复杂但容易忽略:所有 ARIA 属性都要随交互动态更新,静态写死会导致语义失效。测试时用 NVDA + Firefox 或 VoiceOver + Safari,亲自用键盘操作一遍,听它怎么读。










