
valid伪类只对原生表单控件生效,<input>必须带type且有验证属性
浏览器不会凭空判断一个输入框是否“合法”,:valid触发的前提是元素本身参与了 HTML5 表单约束验证(constraint validation API)。比如<input type="email">输入foo@bar会被判为无效,但<input type="text">永远:valid——它没内置校验规则。
常见踩坑:加了required却没设type,或用了type="text"又指望邮箱格式校验。正确做法是:
-
type选对:邮箱用email,数字用number或range,URL用url - 配合约束属性:
required、min/max、pattern(注意pattern值是正则字面量,不带/斜杠) - 避免自定义
type如type="my-input"——它会被当text处理,失去所有验证能力
样式优先级问题::valid/:invalid 会被内联style或!important覆盖
很多人写了input:valid { color: green; }却没反应,其实样式被其他规则压住了。浏览器默认的:valid没有权重优势,它和普通类选择器一样按 CSS 层叠规则走。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 把
:valid和:invalid规则写在样式表靠后位置,或用更具体的选择器,比如form input:valid - 避免在元素上直接写
style="color: red"——这会强制覆盖伪类颜色 - 调试时用浏览器开发者工具检查“Computed”面板,确认
:valid状态是否被识别,以及哪条规则最终生效
动态提示不能只靠:valid,用户未输入时:invalid也成立
这是最容易忽略的逻辑陷阱::invalid在required字段为空时就触发,不是“输错才红”。所以刚打开页面,所有必填项默认都是红色——体验很奇怪。
解决方案是结合:user-invalid(Chrome 102+/Firefox 119+ 支持)或用 JavaScript 控制状态类:
- 现代方案:用
input:user-invalid替代input:invalid,它只在用户**修改过且当前值非法**时匹配 - 兼容方案:监听
input或blur事件,手动加class="touched",再写.touched:invalid - 别忘了
:placeholder-shown可以辅助判断“是否还没动过”,比如input:not(:placeholder-shown):invalid
自定义校验结果需调用setCustomValidity(),否则:valid不响应JS逻辑
如果用 JS 做异步校验(比如检查用户名是否已存在),光改input.value或加class没用。:valid只认表单 API 的校验状态。
关键操作只有一步:
- 调用
input.setCustomValidity("错误信息"):传非空字符串即为无效,""(空字符串)即为有效 - 必须在每次校验后都调用,哪怕只是重置:
input.setCustomValidity("") - 配合
input.reportValidity()可主动触发校验并显示浏览器默认提示(但会打断用户流程,慎用)
例如:
fetch(`/api/check-username?name=${val}`)<br> .then(r => r.json())<br> .then(data => {<br> usernameInput.setCustomValidity(data.taken ? "该用户名已被占用" : "");<br> });
不调这个,哪怕你 JS 判定失败,:invalid也不会变——浏览器根本不知道你校了什么。
复杂点在于:不同浏览器对setCustomValidity的响应时机不一致,有的要等 focusout,有的实时更新;而且一旦设了非空消息,即使清空输入框,:invalid仍保持,必须显式设回空字符串。










