HTML表单校验是浏览器内核集成的约束验证模型,checkValidity()仅读取validity状态不触发UI,reportValidity()则执行完整验证并调用原生提示;pattern对type="number"无效因其值解析早于匹配;required依语义化判空;setCustomValidity('')需每次输入前清空以防错误残留。

HTML表单校验不是靠一段 JS 脚本“监听+判断+弹窗”模拟出来的,而是浏览器内核(Blink/WebKit/Gecko)在 DOM 层直接集成的一套约束验证模型(Constraint Validation API),它有独立的状态机、错误队列和触发时序。
checkValidity() 和 reportValidity() 的底层差异
这两个方法看似只差一个“report”,但行为完全不同:
-
checkValidity()只读取当前元素的validity对象(比如validity.valueMissing、validity.patternMismatch),不触发任何 UI 反馈,也不修改表单提交状态 -
reportValidity()会强制触发一次完整的验证流程:先调用checkValidity(),再检查是否已设置customError,最后调用浏览器原生的错误提示机制(气泡 + 自动 focus + 滚动到字段) - Firefox 不会自动滚动到错误字段,Chrome/Safari 会;这个差异源于各引擎对
reportValidity()的实现细节不同,不是 bug,是规范留白
pattern 属性为什么对 type="number" 完全无效
因为浏览器对 type="number" 的值解析逻辑早于 pattern 匹配阶段:
- 用户输入
123abc时,浏览器内部会尝试转换为 number,失败则设为""(空字符串),而空字符串对任何pattern都返回true -
pattern只作用于type="text"、"email"、"tel"、"url"等文本类类型,这是 HTML 规范明确限定的 - 想校验带格式的数字(如手机号、邮编),必须用
type="text"+pattern+inputmode="numeric"组合,否则既得不到键盘优化,也绕不开解析缺陷
required 和空值判断的真实逻辑
required 判定“空”的标准不是 value === "",而是基于浏览器对控件类型的语义化理解:
立即学习“前端免费学习笔记(深入)”;
-
<input type="number">:输入为空或解析失败(如"abc")时,valueAsNumber为NaN,此时validity.valueMissing为true -
<select></select>:当selectedIndex === 0且第一个<option></option>没有value或value="",也会触发valueMissing -
<input type="file">:files.length === 0才算空,哪怕你手动设了value = ""也没用——value属性在 file 类型上是只读的
setCustomValidity('') 必须每次输入都调用
这是最容易被忽略的底层状态残留问题:
-
setCustomValidity()不是“设置一次就生效”,而是往该元素的内部错误队列里写入一条自定义错误;即使你后来输对了,只要没清空,reportValidity()就一直弹旧错误 - 正确做法是在
input或blur事件开头就调element.setCustomValidity(''),再根据当前值决定是否重新设错 - 如果用
debounce做防抖校验,更要小心:延迟执行前必须先清空,否则用户快速删改时会漏掉清空时机



















