:valid 和 :invalid 伪类需配合 required、type、pattern 等原生验证属性才能生效,用户失焦或提交时触发校验;空值未失焦时不匹配任一伪类;setCustomValidity() 可覆盖校验结果,reportValidity() 强制更新样式;IE 不支持,注意与 JS 状态同步。

怎么让 :valid 和 :invalid 真正生效
它们不会自动触发,必须配合原生表单验证机制——也就是 required、type="email"、pattern 这类属性,否则元素永远处于“未验证”状态,伪类不匹配。
常见错误是只写 CSS 却忘了加验证约束,结果样式完全没反应。浏览器对未交互过的表单控件默认不标记为 :invalid,哪怕它空着且带 required。
- 必须给
<input>加至少一个验证属性(如required、type="number"、minlength="2") - 用户首次失焦(
blur)或提交表单时,浏览器才开始计算有效性,伪类才会切换 -
textarea和select同样支持,但select需设required且首选项无value(比如<option value="" disabled selected>选一项</option>)
:valid / :invalid 的样式时机和边界情况
这两个伪类反映的是「当前值是否通过所有已声明的约束检查」,不是「用户有没有输」。比如 input[type="email"] 值为 "a@",它会立刻匹配 :invalid;而空值在未失焦前既不匹配 :valid 也不匹配 :invalid(属于 :blank,但该伪类尚未广泛支持)。
- 空字符串 +
required→ 失焦后才变:invalid,不是输入瞬间 -
pattern正则不加^$锚点容易误判,例如pattern="a"会让"ab"也通过(只需子串匹配),应写pattern="^a$" - 自定义验证(
setCustomValidity())会覆盖原生校验结果,调用setCustomValidity("")才恢复原生逻辑
和 JavaScript 验证状态同步的坑
CSS 伪类和 JS 的 checkValidity()、validity 对象基本一致,但有一个关键差异:JS 可以主动触发验证(如调用 reportValidity()),而 CSS 伪类只响应浏览器自动触发的验证时机。
立即学习“前端免费学习笔记(深入)”;
- 手动调用
input.reportValidity()会强制触发校验,并使:invalid立即生效(包括显示默认气泡) - 但仅改
input.value不会重算伪类,必须触发一次验证(如dispatchEvent(new Event('input', {bubbles: true}))或等用户失焦) - 服务端返回错误后,用
setCustomValidity("用户名已被占用")设置后,需再调用reportValidity()才能让:invalid样式更新
兼容性和性能注意点
IE 完全不支持这两个伪类,Edge 12+、Chrome 10+、Firefox 4+ 支持良好。Safari 从 5.1 开始支持,但旧版 Safari 对 pattern 的正则引擎较弱,建议避免复杂断言。
- 不要用
:valid/:invalid替代 JS 表单提交拦截——它们不阻止提交,只是视觉反馈 - 大量使用时(如百个输入框),伪类本身无性能问题,但若配合
transition动画,频繁验证可能引发重绘抖动 - 不能用它们检测「是否已填写」,因为合法空值(如非
required的可选邮箱)也会匹配:valid
真正难的不是写对伪类,而是控制好验证时机和与 JS 状态的一致性——尤其在动态表单或异步校验场景下,样式和底层 validity 状态很容易脱节。










