先记住这个答案
当 <select> 有很多相关选项时,用 <optgroup> 把 <option> 分组,并用 label 给每组命名。分组标题用于帮助用户理解类别,不是一个可选项,也不会作为值提交。用户选中的是组内的 <option>,表单发送的是选择框的 name 与该选项的值。给分组加 disabled 可以让整组选项不可选;<optgroup> 本身不能再嵌套另一个分组。
<optgroup>将相关<option>归组label提供分组名称,不是选项值disabled可禁用整个分组- 分组不能再嵌套分组
用城市选择看提交的值
假设配送范围有华东和华南城市。直接列十几个城市,用户要逐条扫;加两个分组能让列表更容易找。要把表单控件的名称、分组名称和选项值分开看:前者决定字段键,分组只作分类,选项决定字段值。
<label for='city'>配送城市</label>
<select id='city' name='city'>
<optgroup label='华东'>
<option value='shanghai'>上海</option>
<option value='hangzhou'>杭州</option>
</optgroup>
<optgroup label='华南'>
<option value='guangzhou'>广州</option>
</optgroup>
</select>选中杭州时,提交值是 city=hangzhou,不会把华东一并提交。label 是给这组选项提供可读标题,不是拿来替代选择框自身的 <label>。若选择框没有外部标签,用户仍可能不知道整个控件在问什么。
禁用分组与占位选项
如果暂时不配送华南,可在对应 <optgroup> 上加 disabled,组内选项便不能被用户选择。单独只禁用广州,则把 disabled 写在对应 <option> 上。两者作用范围不同,检查表单时要看最终选中值是否还有效,不能只凭文字被灰化就认为提交状态一定符合业务要求。
有些表单用第一项提示用户先选择,例如空值的请选择。对于单选且 required 的常见下拉框,占位标签选项需要成为 <select> 的直接子项,不能藏在 <optgroup> 中;否则会影响浏览器对占位选项的识别。分组应从真正可选的类别开始。选项数量少时也不必强行分组,额外标题反而会让列表更长。
分组能力的边界
<optgroup> 只有一层分组语义,不能用多层嵌套实现地区省市树。如果选项层级复杂,可以分两个联动选择框,或设计可访问的专用选择组件。原生控件的弹层外观由系统和浏览器决定,不应期待分组标题能放任意图标、按钮或复杂布局。
有些新式可定制选择框允许在分组中放 <legend> 提供标题,但这属于不同的渲染模式。普通面试回答和常规 <select> 写法,用 label 是最清晰的规则。不要因为某个浏览器接受缺少 label 的标记,就把未命名分组当成可用设计。
容易答错的地方
- 以为分组标题能被选中
<optgroup>只是容器和标题。键盘或鼠标最终选择的是其下的<option>;分组名不会作为选中值出现在表单数据中。- 以为 optgroup 可以无限嵌套
- 标准的分组不能再包一层
<optgroup>。需要多级分类时,应调整交互结构,不要把嵌套标签当树形选择器。
面试官还会怎么问?
禁用整个分组和禁用一个选项有什么区别?
前者把组内所有选项置于不可选状态,后者只影响对应选项。业务上禁用整片地区时可用前者,单个城市维护中可用后者。
optgroup 的 label 能替代 select 的 label 吗?
不能。分组 label 说明局部选项类别;外部 <label for> 说明整个控件要用户选择什么,两层信息都可能需要。
required 的请选择选项放进 optgroup 可以吗?
对于常见单选下拉框,不应这样放。空值占位选项应直接位于 <select> 下,分组用于真正的候选项;提交时再用 required 检查用户是否选了有效值。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。