optgroup 是 HTML 唯一标准分组方式,label 必须非空且 trim 后有效、必须为 select 直接子元素、disabled 在 Safari 无效、iOS 原生不显示分组标题。

optgroup 是 HTML 原生实现下拉选项逻辑分组的唯一标准方式,但写错一行就可能完全失效——不是没效果,而是浏览器直接忽略整组。关键不在“怎么加”,而在“怎么不踩坑”。
label 属性必须非空且无空白
浏览器只靠 label 渲染分组标题,没它等于没写 optgroup。以下写法全部静默失败:
-
<optgroup><option>苹果</option></optgroup>(漏写 label) -
<optgroup label=""><option>香蕉</option></optgroup>(空字符串) -
<optgroup label=" "><option>橙子</option></optgroup>(纯空格)
动态生成时务必提前 trim 并校验:if (!group.label?.trim()) return;中文 label 控制在 2–4 字,如“水果”“华东”,避免“其他分类”这类模糊表述。
必须是 select 的直接子元素
optgroup 不是独立控件,只能平级出现在 select 内部,且内部只允许 option。任何嵌套或包裹都会被浏览器跳过:
- ❌
<div><optgroup label="蔬菜"></optgroup></div> - ❌
<select><optgroup><optgroup label="根茎"></optgroup></optgroup></select> - ✅ 正确结构:
<select><optgroup label="水果"><option>苹果</option></optgroup></select>
模板引擎中用条件块时,注意别让标签前后断裂,比如 {% if group %}<optgroup label="{{group.name}}">...</optgroup>{% endif %} 必须确保 group.name 已求值且非空。
disabled 行为有平台差异,不能全信
给 optgroup 加 disabled,Chrome/Firefox/Edge 会禁用整组选项(置灰+不可选),但 Safari 16 及更早版本完全忽略该属性——组内 option 仍可点、可键盘聚焦、可提交。
- 全平台一致禁用?必须手动给每个 option 加
disabled,再配合 CSS 弱化视觉表现 - optgroup disabled 不阻止键盘上下键切换到该组内选项,表单提交前仍需服务端校验值合法性
- 标题文字始终可见,这不是 bug,是规范行为;想彻底隐藏某组,只能 JS 移除节点或服务端不输出
iOS 原生 select 不显示分组信息
iOS 系统弹出的原生 Picker 完全不渲染 optgroup label,所有选项扁平排列,“苹果”和“胡萝卜”并列,用户无法分辨归属。这不是兼容性问题,而是 Safari 明确的设计限制。
- 若业务强依赖分组语义,必须放弃原生 select,改用 JS 下拉组件(如 Choices.js、tom-select)
- 若坚持用原生,确保每个 option 文本开头含关键信息,例如
<option>? 苹果</option>或<option>[水果]苹果</option>,避免仅靠分组标题传递含义 - 屏幕阅读器仍能播报 label(如“水果,组合”),这对无障碍友好,但对普通 iOS 用户无效

















