原生html表单校验需谨慎使用:checkvalidity()仅返回布尔值,须调用reportvalidity()触发ui提示;safari 15.4+才支持自定义校验提示;隐藏字段不显示提示;setcustomvalidity('')清空校验但易误用;title属性不可靠,应统一用setcustomvalidity();禁用novalidate时须手动同步aria-invalid和aria-describedby以保障无障碍。

原生 HTML 表单校验不是“超好用”,而是容易被误用——多数人直接写 required 或 type="email" 就以为完事了,结果在 Safari 里不触发、在 Chrome 里提示语难改、提交时绕过校验还静默失败。
为什么 checkValidity() 调用后没反应?
这是最常卡住的点:你写了 form.checkValidity(),控制台返回 true 或 false,但页面没弹提示、没高亮错误字段——因为原生校验只在表单提交(submit 事件)或显式调用 reportValidity() 时才渲染 UI 提示。
-
checkValidity()只做逻辑判断,不触发浏览器默认错误气泡 - 想手动触发提示,必须用
reportValidity()(所有现代浏览器支持) - Safari 15.4+ 才修复了
reportValidity()对自定义setCustomValidity()的支持,旧版会静默忽略 - 如果字段有
display: none或visibility: hidden,即使校验失败,reportValidity()也不会显示提示
setCustomValidity() 的空字符串陷阱
很多人这样写校验逻辑:
input.addEventListener('input', () => {
if (input.value.length
<p>问题出在最后一行:<code>setCustomValidity('')</code> 不等于“清除校验”,它等价于 <code>setCustomValidity('')</code> —— 浏览器仍认为该字段处于“自定义校验状态”,后续 <code>checkValidity()</code> 可能持续返回 <code>false</code>,哪怕值已合法。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill7761" title="Article To Html"><img
src="https://img.php.cn/upload/skill/000/000/081/179168408018805.jpg" alt="Article To Html" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill7761" title="Article To Html" class="overflowclass">Article To Html</a>
<p class="overflowclass">文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。</p>
</div>
<a rel="nofollow" href="/xiazai/skill7761" title="Article To Html" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 正确清空方式是
setCustomValidity('')后立即调用input.reportValidity()(可选),但更稳妥的是用input.setCustomValidity('')+ 触发一次input.checkValidity()来重置内部状态 - 更好的实践:只在非法时设错,合法时一律用
input.setCustomValidity(''),且确保每次设置都覆盖前值 - 注意:
setCustomValidity()的字符串一旦非空,该字段就永远算“无效”,直到你显式设回空字符串
Chrome 和 Firefox 对 title 属性的提示差异
你给 input 加了 title="请输入手机号",发现 Chrome 在悬停时显示它,Firefox 却只在校验失败气泡里显示——这是因为 title 仅作为辅助提示,不是校验文案来源。
- 校验失败时的提示文案,优先级是:
setCustomValidity()设置的字符串 > 浏览器内置文案(如 “Please fill in this field”)>title属性(仅部分浏览器 fallback 使用) - Firefox 完全忽略
title作为校验提示;Chrome 115+ 开始也逐步弱化它 - 想统一控制提示文字,必须用
setCustomValidity(),别依赖title - 如果用了 i18n,记得在语言切换时重新对每个字段调用
setCustomValidity()
禁用原生校验后,如何保留无障碍支持?
很多项目因样式/交互需求,直接加 novalidate 属性或用 event.preventDefault() 拦截 submit,结果屏幕阅读器无法感知错误状态,aria-invalid 和 aria-describedby 也没同步更新。
- 保留
novalidate是可以的,但必须手动管理aria-invalid="true/false"和关联的错误文案元素 - 每次校验后,对非法字段设
input.setAttribute('aria-invalid', 'true'),并用input.setAttribute('aria-describedby', 'error-id')指向对应<div id="error-id"> <li>别忘了在输入恢复合法时,同步清除 <code>aria-invalid和aria-describedby,否则辅助技术会持续报错 - 原生校验的
:valid/:invalid伪类在novalidate下失效,需用 JS 切换 class 控制样式
真正难的不是写校验逻辑,而是让校验状态、UI 反馈、辅助技术支持、多浏览器行为全部对齐——尤其当字段动态增删、国际化切换、表单分步提交时,一个没同步的 setCustomValidity('') 就能让整个流程卡在不可见的 invalid 状态里。










