:valid 和 :invalid 伪类仅在浏览器原生验证触发后生效,依赖 HTML 验证属性(如 required、type="email")和用户交互;无验证属性或 type="text" 等不校验类型下样式不生效;JS 自定义校验需调用 setCustomValidity("") 或 reportValidity() 才更新状态。

:valid 和 :invalid 伪类本身不会“主动变色”,它们只在浏览器完成原生验证后才匹配——而验证是否发生、何时发生,完全取决于你有没有配对的 HTML 验证属性和用户交互行为。没加 required、没设 type="email"、或用了 type="text" 却指望邮箱校验,样式就永远不生效。
为什么写了 input:valid { color: green; } 却没反应
常见原因不是 CSS 写错了,而是 HTML 没达标:
-
<input>必须带至少一个验证属性:required、type="email"、minlength="2"、pattern="^\d{6}$"等 -
type="text"是“无校验型”,哪怕加了pattern,也得确保 pattern 值不带/(例如写pattern="^[a-z]+$",不是pattern="/^[a-z]+$/") - 自定义
type如type="custom-input"会被降级为text,直接失去所有验证能力 - 样式被更高优先级规则覆盖:检查开发者工具的 “Computed” 面板,确认
:valid状态存在,且对应规则未被或!important压制
:invalid 一打开页面就全红?这是正常行为,不是 bug
只要字段带 required,页面加载时值为空,它就满足 :invalid 条件——浏览器认为“未通过校验”,不是“还没校验”。这不是失效,是设计如此。
- 想延迟提示?用
input:not(:placeholder-shown):invalid(前提是设置了placeholder) - 更稳妥的兼容方案:
input.touched:invalid,配合 JS 在blur或input后加 class - 现代方案可用
input:user-invalid,但注意 Chrome 102+ / Firefox 119+ 才支持,Safari 当前仍不支持 - 别用
:focus:invalid做唯一判断——用户聚焦又删空,依然会触发,体验断层
JS 自定义校验后,:valid 不更新?只差一步调用
手动用 JS 检查用户名是否重复、是否已注册,光改 input.value 或加 class 没用。:valid 只认表单 API 的状态。
立即学习“前端免费学习笔记(深入)”;
- 必须调用
input.setCustomValidity("错误信息"):传非空字符串 → 触发:invalid;传空字符串""→ 恢复为:valid - 每次校验都要调用,包括“校验通过”的情况——漏掉
setCustomValidity(""),状态就卡死 - 调用后若需立即刷新样式(比如服务端返回错误),补一句
input.reportValidity(),它会强制重算并激活伪类 - 注意:
reportValidity()会弹默认气泡提示,影响流程;仅需样式更新时,可省略它,只靠setCustomValidity+ 用户后续交互(如 blur)来触发
真正容易被忽略的点是::valid/:invalid 的触发时机由浏览器控制,不是实时监听输入。哪怕你用 JS 改了值,不触发验证逻辑(比如不 blur、不 submit、不调 reportValidity()),伪类就不会翻转——这和 :checked 或 :hover 的响应逻辑完全不同。


















