先记住这个答案
当原生属性表达不了业务条件,可以用 input.setCustomValidity("具体错误")。只要消息非空,validity.customError 就为真,表单提交会被阻止。用户修改输入后,浏览器不会自动清除这条脚本写入的错误;代码要重新检查条件,正确时调用 setCustomValidity("")。需要主动展示反馈时,再调用 reportValidity()。
- 非空消息令
customError为真 - 清空字符串解除自定义错误
- 输入变化时应重新计算错误
reportValidity()可显示提示
两个字段必须一致
确认密码不是单个输入框能靠 required 或 pattern 独立判断的规则,需要比较两个字段。比较失败时把错误设在确认框上,用户修改任一相关字段后重新计算。
<input id="password" type="password" required>
<input id="confirm" type="password" required>
<script>
const a = document.querySelector('#password')
const b = document.querySelector('#confirm')
function validate() {
b.setCustomValidity(b.value && a.value !== b.value ? '两次密码不一致' : '')
}
a.addEventListener('input', validate)
b.addEventListener('input', validate)
</script>两个值不一致且确认框非空时,确认框保持自定义错误;一致后写入空字符串,恢复其他原生约束的正常判断。空确认框仍由 required 处理,不需要重复写一条自定义错误。
这里的脚本只负责浏览器端即时反馈。服务端依然要核对字段,因为任何前端校验都能绕过。
展示提示与设置状态是两步
setCustomValidity() 主要改变控件的校验状态和消息,不等于每次调用都立刻弹出气泡。用户提交或代码调用 reportValidity() 时,浏览器才会按当前状态展示交互式提示。
若业务自己绘制错误文字,也应让它与当前输入同步,并在修正后清除。不要把浏览器本地化的 validationMessage 字符串拿来判断错误类型;读 validity 的具体标志更可靠。
调用 setCustomValidity('两次密码不一致') 后,即使用户后来把值改对了,只要没有再调用 setCustomValidity('') 清空,控件仍是无效状态。把设置错误与清除错误放在同一套 input 或 change 逻辑里,别只在提交失败时设置一次。validationMessage 是浏览器当前给出的本地化提示文本,可以展示给用户,但不要在代码里解析提示字符串判断是哪种错误;应看 validity 对象上的具体标志。
容易答错的地方
- 值改对后忘了清空旧错误
- 自定义消息会一直保留,导致表单始终不能提交。正确状态必须写回空字符串。
- 以为设置消息就立刻显示气泡
- 需要提交触发校验或主动调用
reportValidity();消息本身先改变的是有效性。
面试官还会怎么问?
怎么判断是自定义错误?
读取 input.validity.customError,不要比较提示文本。
清空自定义错误后是否一定有效?
不一定。required、pattern 等其他原生约束仍可能失败。
应该在 input 还是 submit 时重新校验?
相关字段变化时及时更新状态,提交时再做最后检查;服务端仍应验证。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。