是语义化、无障碍友好的原生下拉分组方案,需严格遵循结构规范:仅直接嵌套于内、包裹、label属性不可为空且不解析html;禁用整组用disabled属性;动态插入须用select.add();样式定制受限,高级功能应选用js组件。

用 <optgroup></optgroup> 标签就能对下拉选项做原生分组,它不是装饰,而是语义化、无障碍友好的标准方案。
必须写对结构和属性
<optgroup></optgroup> 只能直接放在 <select></select> 里面,不能套在 <div> 里,也不能嵌套另一个 <code><optgroup></optgroup>。它只允许包裹 <option></option>,其他内容(包括空格、注释、文本)都可能让浏览器静默忽略整组。
- label 属性不可省略,且不能是空字符串、纯空格或仅含空白符;写成
label=""或label=" "就等于没写 - 中文 label 建议控制在 2–4 字,比如“华东”“待审核”,避免“其他分类”这类模糊表述
- label 值不解析 HTML,
label="<strong>水果</strong>"会原样显示为文字,不会加粗
禁用整组要加在 optgroup 上
给 <optgroup disabled></optgroup> 加 disabled,内部所有 <option></option> 都会变灰且不可选,但分组标题(label)依然可见——这是规范行为,不是失效。
- 不要用 CSS 模拟禁用,比如
style="pointer-events: none",键盘仍可聚焦、屏幕阅读器可能误读 - 如果想让某组彻底不出现,得用 JS 移除整个
<optgroup></optgroup>节点,或服务端不输出该标签 - 单独禁用某个选项,直接给对应
<option disabled></option>加属性即可
动态插入要注意方法和顺序
JS 创建分组时,不能用 appendChild() 往 <select></select> 添加 <optgroup></optgroup>,否则会报错或静默失败。
- 正确做法:先创建
<optgroup></optgroup>元素,再显式赋值optgroup.label = "xxx",最后调用select.add(optgroup) - 往
<optgroup></optgroup>内添加<option></option>时,可以用optgroup.appendChild(option) - 设置默认选中项(如
select.value = "sh")前,确保对应<option></option>已插入 DOM,否则赋值会被丢弃
样式定制空间很小,别强求
浏览器对 <optgroup></optgroup> 的 CSS 支持非常有限:
- color、font-weight 在部分浏览器中可用,但 background-color、padding、margin 基本无效
- iOS Safari 原生下拉菜单中,
<optgroup></optgroup>的 label 通常完全不显示,只渲染<option></option> - 需要图标、悬停、搜索等高级功能,应改用 JS 实现的下拉组件(如 Choices.js),而非硬改原生样式











