:valid/:invalid 仅响应浏览器原生校验,需 input 具备 type(如 email)、required、pattern 等约束才生效;pattern 不加斜杠;空 required 初始即 :invalid;自定义校验须用 setcustomvalidity()。

:valid 和 :invalid 能实现基础实时提示,但“实时”仅限于浏览器原生校验触发的时机——不是用户每敲一个键都变色,而是值变化后满足/违反约束时立即响应。它不依赖 JS,但极度依赖 HTML 属性配置是否正确。
为什么写了 :valid 却没反应?
根本原因几乎总是:输入框没参与原生校验。浏览器不会凭空判断合法性。
-
<input type="text">永远:valid——它没有内置规则,required单独存在也不够 - 必须同时有
type(如email、url、number)和/或required、pattern、min/max等约束属性 -
pattern值是正则字面量,不能带前后斜杠:pattern="[0-9]{6}"✅,pattern="/[0-9]{6}/"❌ - 自定义
type(如type="phone")会被降级为text,失去所有验证能力
:invalid 一加载就红?这是正常行为,不是 bug
空的 required 字段在页面渲染完成时即为无效状态,所以 :invalid 立刻匹配。这不是延迟问题,而是规范定义。
- 想避免初始红色?用
input:invalid:not(:placeholder-shown),前提是设置了placeholder - 更精准的方案是
input:user-invalid(Chrome 102+ / Firefox 119+ / Safari 17.4+),但它在旧版 Safari 和部分安卓 WebView 中不支持 - 若需全兼容,放弃伪类驱动,改用 JS 监听
input或blur,手动加is-invalid类
边框变色要平滑,但别给 :invalid 加 transition
错误反馈需要明确、即时。过渡动画加在 :valid 或 :focus 上能提升体验,但加在 :invalid 上容易造成“刚打字就渐变红”的压迫感。
- 推荐写法:
input { transition: border-color 0.2s ease; },再单独重置input:invalid { transition: none; } - 注意优先级:如果某条规则写了
border-color: red且比var(--border-color)更具体,变量就会被覆盖 - 内联样式(
style="border-color: blue")会直接压过var(),调试时先检查 computed styles 中最终生效的 border-color 来源
自定义校验逻辑(如用户名是否存在)无法触发 :valid
:valid 只认 Constraint Validation API 的状态,JS 改 value 或 class 完全无效。
- 必须调用
input.setCustomValidity("错误信息"):传非空字符串即标记为无效,传空字符串""即标记为有效 - 每次校验后都得调用,哪怕只是重置;漏掉一次,状态就卡住
- 配合
input.reportValidity()可主动触发浏览器默认提示框(含 tooltip),但会打断用户操作流 - 异步校验(如 fetch 检查用户名)完成后,必须在回调里调用
setCustomValidity,不能只改 UI
真正难的不是写对那几行 CSS,而是理解浏览器何时认定一个值“合法”——这个判定权不在你写的样式里,而在 type、required、setCustomValidity 这三者的组合逻辑中。任何脱离这三层的“实时变色”尝试,都会在某个边界 case 下失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











