浏览器原生 :valid/:invalid 伪类默认首次提交前不触发,空 required 输入初始为 :valid;需用 :user-invalid、:focus:invalid + :placeholder-shown 或 js 添加 class 实现即时验证,且不作用于 disabled/readonly 元素。

表单字段没填就显示红色边框?别急着写JS
浏览器原生的 :valid 和 :invalid 伪类会在输入控件通过或未通过其内置约束(如 required、type="email"、min/max)时自动切换状态——但关键在于:它们默认在用户**首次提交表单前不触发**。也就是说,空的 required 输入框初始是 :valid(因为尚未验证),直到用户交互或提交后才变 :invalid。
想实现“输入即验”,必须配合 :user-invalid(Chrome 102+、Firefox 119+ 支持)或退而求其次用 :focus:invalid + :placeholder-shown 模拟。更稳妥的做法是加一个 class 触发器,比如 .js-validate,再用 CSS 配合 JS 初始标记:
input:invalid:not(:placeholder-shown):not(:focus) {
border-color: #e53e3e;
}
input:valid:not(:placeholder-shown) {
border-color: #38a169;
}
:invalid 样式被 input[type="number"] 的默认行为干扰?
当用户在 input[type="number"] 中输入非数字字符(如 "abc"),该字段会变成空值(value === ""),此时即使设了 required,:invalid 也不一定立即生效——因为浏览器认为它“暂时无效但可恢复”,尤其在失去焦点前。
- 确保同时设置
required和pattern(例如pattern="[0-9]*")来强化校验逻辑 - 避免只依赖
type="number"做业务校验;后端仍需验证,前端建议用inputmode="numeric"+ 自定义 JS 补充 - Chrome 下
:invalid对空 number 输入框的响应比 Firefox 慢半拍,加:not(:placeholder-shown)能缓解
为什么 :valid/:invalid 不影响 disabled 或 readonly 元素?
这是规范行为::valid 和 :invalid 只对**可交互且参与约束验证**的元素生效。一旦加了 disabled,浏览器直接跳过验证流程,伪类永不匹配;readonly 虽保留值,但若字段无 required 或其他约束,也不会触发 :invalid。
常见误操作:
- 给
input[readonly]加:invalid样式——完全无效,应改用 JS 动态加 class - 用
fieldset[disabled]包裹整个表单块,导致内部所有:valid/:invalid失效 - 期望
textarea在内容为空时自动匹配:invalid——除非显式加required,否则不会
兼容性兜底和样式优先级陷阱
:valid/:invalid 在 IE 完全不支持,Safari 15.4 之前对 pattern 的 :invalid 响应有延迟。更重要的是:它们的 CSS 优先级和普通类名一样,容易被后续规则覆盖。
实操建议:
- 把验证样式写在表单基础样式之后,避免被重置(比如不要让
input { border: 1px solid #cbd5e0; }出现在input:invalid规则前面) - 用
input:not([type="hidden"]):invalid排除隐藏域干扰 - 移动端 Safari 对
:user-invalid支持不稳定,生产环境建议用 JS 监听input和blur事件并 toggleis-invalidclass 更可控
最易被忽略的一点:伪类验证只看 HTML 约束,不执行自定义逻辑。比如邮箱格式正确但域名不存在,:valid 依然为真——这类校验必须走 JS + API,不能指望 CSS 解决。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











