required校验通过是因为浏览器将首个非空value的option视为初始值;要真正生效,必须添加value为空字符串的提示选项且置于首位,不加selected、disabled或hidden。

select加required后,为什么默认选中第一个选项却没报错?
因为浏览器把第一个 <option></option> 的值当作了初始值——哪怕它只是个提示文字,只要 value 不为空,校验就通过了。比如 <option value="active">启用</option> 被自动选中,用户不点开也能提交。
要让 required 真正生效,必须有一个明确的“空状态”可被选中,且这个状态不能是有效业务值。
- 用
<option value="">-- 请选择 --</option>,value必须是空字符串 - 这个空选项要放在最前面,避免用户误以为后面某个选项是默认项
- 不要给它加
selected,否则页面一加载就“看似已选”,但实际是无效值 - 更别加
disabled或hidden—— disabled 会让该选项无法被提交,hidden 在部分浏览器中行为不可靠
required + select 的正确 HTML 写法示例
下面这段是能触发原生校验的最小可靠结构:
<select name="category" required><option value="">-- 请选择分类 --</option> <option value="tech">技术</option> <option value="design">设计</option></select>
关键点:
-
required加在<select></select>上,不是<option></option> - 空
<option></option>的value是""(双引号中间无空格) - 文本内容可以是任何提示语,但不能误导用户认为它是可提交的有效值
- 如果服务端返回了预设值(比如用户上次选的是
design),应由 JS 动态设置select.value = "design",而不是靠selected属性硬编码
移动端和旧版 iOS Safari 的兼容性坑
某些旧版 iOS Safari 对 required 在 <select></select> 上的支持不稳定,可能跳过校验或点击后不弹出键盘式选择器。
稳妥做法是补一层防御性逻辑:
- 始终配一个空
<option value=""></option>占位 - 用
oninvalid和oninput手动控制提示文案:<select required oninvalid="this.setCustomValidity('请选择有效分类')" oninput="this.setCustomValidity('')"></select> - JS 中调用
checkValidity()前先确认select.value !== "",别只信reportValidity()的返回值
selected 属性和 required 能共存吗?
能,但必须小心顺序和语义。如果写成这样:
<select required><option value="">-- 请选择 --</option> <option value="tech" selected>技术</option></select>
结果是:页面加载时显示“技术”,required 不报错——因为它确实有值。但这违背了“强制用户主动选择”的初衷。
所以:
- 想让用户必须点一次下拉框再选,就不要给任何
<option></option>加selected - 想预设值(比如编辑表单),就用 JS 设置
select.value = "tech",并确保空选项仍存在、value为空 -
selected是渲染态控制,required是校验态控制,二者作用域不同,混用容易自相矛盾
最易被忽略的一点:表单重置(form.reset())会把 select 恢复到最初 HTML 中的 selected 状态,而不是 JS 设置后的值——如果你靠 JS 设了默认值,又用了 reset(),得手动同步回 JS 状态。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











