required属性对select生效需同时满足:空值option必须disabled+selected+value="",否则校验失效;仅selected或仅disabled均会导致空值提交或立即报错。

required 属性本身不会阻止选择空文本选项——它只在提交时校验,而是否“被选中”取决于 <option></option> 的 selected 和 disabled 状态。关键不是“阻止选择”,而是让空值选项无法成为有效提交项。
select 的 required 校验失败的根本原因
浏览器对 <select required></select> 的判定逻辑是:只要当前选中的 <option></option> 的 value 为空字符串(""),就视为未填写,触发校验失败。但这个“当前选中”必须是用户可交互、可提交的选项——如果唯一带 selected 的选项还加了 disabled,它就不会参与提交,也不会被当作“已选中值”供校验参考。
- 只写
<option value="" selected>请选择</option>:该选项可被选中、可提交空值,required会认为“已填”,但实际提交的是空字符串 - 只写
<option value="" disabled>请选择</option>:该选项不可选、不参与提交,页面加载后没有默认选中项,required立即报错 - 正确写法必须同时含
selected和disabled:确保它高亮显示为占位,又不被提交
required 生效的前提:空值 option 必须 disabled + selected
这是唯一能让 required 正常工作的组合。浏览器会把该 <option></option> 当作初始视觉状态,但因其 disabled,不计入表单数据;用户必须手动切换到其他非 disabled 选项,才算“有效选择”。
-
value必须是空字符串:value="",不能是空格或" " - 不能漏掉
disabled:否则用户点一下就能重新选中它,提交空值 - 不能漏掉
selected:否则页面一加载就没有默认项,required立即失败 - 后续所有
<option></option>都不能带selected,否则会覆盖占位项
为什么加了 required 还能提交空值?常见配置错误
最常踩的坑是混淆了“显示”和“提交”逻辑。以下写法都会导致校验失效或行为异常:
-
<option value="{{database[1]}}" selected disabled hidden></option>:用了hidden,但hidden不影响提交逻辑,且部分浏览器忽略它;disabled已足够,hidden多余且危险 -
<option value="" selected></option>没加disabled:用户没动下拉框,提交的就是空字符串,required认为“已填”,绕过校验 - 动态渲染时,服务端模板(如 Jinja、Django)把占位项也循环进了数据列表,导致多个
selected冲突 - JS 动态设置
select.value = ""后没同步重置selectedIndex或清除其他selected属性,破坏了原生校验上下文
移动端或旧 WebView 中 required 不生效怎么办
微信内置浏览器(尤其 iOS 旧版)、部分安卓 WebView 会直接忽略 required,form.checkValidity() 甚至返回 true。这时不能只依赖原生属性:
- 提交前手动调用
form.checkValidity(),并 fallback 到 JS 校验:if (!select.value) { /* 提示并 focus */ } - 对
<select></select>绑定change事件,记录是否被用户主动操作过,避免静默提交 - 后端必须做非空校验——
required只是前端体验优化,不是安全边界
真正起作用的从来不是 required 这个属性本身,而是它背后那一套“占位项必须 disabled + selected + value=""”的约定。漏掉任意一环,空值就会悄悄提交。别信“写了就管用”,得看 DOM 实际状态是否满足这三条。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











