原生是无障碍首选,只需用aria-labelledby或aria-label补充可访问名称,并正确使用required、disabled等原生属性;自定义下拉需完整实现aria combobox模式,否则易出错。

下拉框必须用 <select></select> 配合 <option></option> 才能正确支持 ARIA
纯 <div> 模拟的“下拉”哪怕加了 <code>role="combobox",屏幕阅读器也无法可靠识别选项、无法用方向键切换、不触发 aria-expanded 状态变更。原生 <select></select> 自带语义和键盘行为,是无障碍首选。
如果必须用自定义下拉(比如需要复杂渲染),那就得完整实现 combobox 的 ARIA 模式:管理焦点、监听 ArrowDown/Up、同步 aria-activedescendant、手动控制 aria-expanded —— 工作量大且易出错。
<select></select> 加 aria-label 或 aria-labelledby 是最简有效方案
原生 <select></select> 本身已具备 role="combobox" 和基础语义,只需补全可访问名称(accessible name)即可被读屏软件正确播报。
- 有可见标签时,优先用
aria-labelledby指向<label></label>的id:<label id="city-label">城市</label> <select aria-labelledby="city-label"> <option value="bj">北京</option> <option value="sh">上海</option> </select>
- 无可见标签(如表单内嵌图标下拉),用
aria-label直接提供文字:<select aria-label="筛选状态"> <option value="all">全部</option> <option value="active">启用</option> </select>
- 避免同时用
aria-label和<label></label>,会造成重复播报
aria-required 不要乱加,required 属性已自带语义
<select required></select> 会被读屏软件自动识别为“必填”,并播报“必填”或对应本地化提示。额外加 aria-required="true" 不仅冗余,还可能干扰部分旧版读屏器判断。
立即学习“前端免费学习笔记(深入)”;
真正需要 aria-required 的场景极少,仅当无法使用原生 required 属性(比如自定义下拉 + JS 校验)时才考虑,且必须配合完整 ARIA combobox 实现。
- ✅ 正确:
<select required></select> - ❌ 多余:
<select required aria-required="true"></select> - ⚠️ 风险:
<div role="combobox" aria-required="true"> —— 若未实现完整交互逻辑,读屏器会说“必填”,但用户根本无法操作 <h3>禁用状态要用 <code>disabled,别只靠aria-disabled="true"<select disabled></select>既禁用交互,又让读屏器播报“已禁用”,这是标准行为。而aria-disabled="true"只影响语义播报,不阻止焦点进入、不阻止键盘操作 —— 用户仍可按 Tab 进入,再按空格或方向键触发异常行为。若需动态控制禁用状态,用 JS 操作
element.disabled = true,而非设置aria-disabled。- ✅ 安全:
<select disabled><option>不可选</option></select> - ❌ 危险:
<select aria-disabled="true"><option>看起来禁用</option></select>(用户仍能聚焦并尝试操作)
ARIA 不是装饰品,加错比不加更糟。对原生
<select></select>,核心就两件事:确保可访问名称(aria-labelledby或aria-label),用好原生属性(required、disabled、multiple)。所有绕过原生语义的“增强”,都会把简单问题变成兼容性噩梦。 - ✅ 安全:











