optgroup标签必须嵌套在select内且为直属子元素,仅能包裹option,label属性必填且不可为空,disabled属性可禁用整组选项,但ios safari原生下拉中不显示label。

optgroup 标签必须嵌套在 select 里,不能单独用
很多人直接写 <optgroup></optgroup> 就报错,根本原因是它只是语义分组容器,没有独立渲染能力。浏览器只认它作为 <select></select> 的直属子元素,且只能包裹 <option></option>,不能套 <div> 或其他标签。
<p>常见错误写法:<br><code><optgroup label="水果"><div><option>苹果</option></div></optgroup> → 浏览器会忽略 <div>,甚至整个 <code><optgroup></optgroup> 可能不生效。
-
<select></select>是唯一合法父容器 - 所有
<optgroup></optgroup>必须直接写在<select></select>开始标签后、结束标签前 - 同一级不能混用
<option></option>和<optgroup></optgroup>(即不能有“平级选项+分组”这种结构)
label 属性是必需的,且不能为空字符串
省略 label 或写成 label="",Chrome/Firefox 都会显示一个空行或“未命名组”,视觉上像多了一条分割线但没文字,容易误以为样式坏了。
正确示例:
<optgroup label="前端框架"> <br> <option value="vue">Vue</option> <br> <option value="react">React</option> <br> </optgroup>
-
label值会被渲染为灰色不可选文字,通常加粗或带缩进 - 不支持 HTML 标签,
label="<strong>重要</strong>"会原样输出字符,不会加粗 - 中文、英文、数字、短横线都可安全使用;避免过长(建议 ≤ 12 字),否则在小屏幕可能截断
禁用整个分组要用 disabled 属性,不是 CSS
想让“已下架商品”这组选项全部不可点,很多人去写 style="color: #999; pointer-events: none;",这只能骗过眼睛——键盘仍可 Tab 进去、回车还能触发,还可能被屏幕阅读器误读。
真正有效的是原生 disabled:
<optgroup label="已下架商品" disabled> <br> <option value="nokia">诺基亚</option> <br> </optgroup>
- 加了
disabled后,整组<option></option>都变灰且无法聚焦/选择 - 该属性对
<optgroup></optgroup>本身有效,对内部<option></option>不再需要单独加disabled - 注意:disabled 分组里的值不会被表单提交,JS 读
select.value时也完全忽略它们
移动端 Safari 对 optgroup 的样式支持很弱
iOS 系统自带的下拉菜单(即点击 <select></select> 弹出的原生 Picker)根本不显示 <optgroup></optgroup> 的 label 文字,用户只看到一堆平铺选项,分组逻辑彻底丢失。
这不是 bug,是 Safari 的设计取舍。如果你的应用重度依赖分组语义(比如按地区筛选城市),就得考虑降级方案:
- 用 JS 模拟下拉(如
select2、choices.js),但会增加包体积和维护成本 - 改用
<details><summary></summary></details>+ 列表做手风琴式展开,兼容性好且可控 - 最简方案:在每个
<option></option>文本前加前缀,例如<option>【水果】香蕉</option>,牺牲一点美观保功能
别指望 CSS 能修好 Safari 的这个限制——optgroup 在 iOS 原生控件里就是个摆设。











