:valid 和 :invalid 不实时,仅响应原生校验状态变更,空值即触发 :invalid;user-invalid 可解决初始误判但 Safari 16.4+ 才支持;推荐用 :not(:focus) 分离验证与聚焦样式,type="number" 易因中间态变红,应改用 type="text"+pattern 或 JS 校验。

:valid 和 :invalid 本身不“实时”,它们只响应浏览器原生校验状态变更,且初始空值就触发 :invalid —— 所以直接写 input:invalid 会一加载就红边框,这不是 bug,是规范行为。
为什么刚聚焦就显示红色?
因为 :invalid 判定基于 HTML 约束(如 required、type="email"),只要字段有约束,空值即被视作无效。页面渲染完成时,这个状态就已生效。
- 不是 JS 没跑,也不是 CSS 写错了,是浏览器按标准执行
-
:user-invalid可精准解决(仅用户编辑后才匹配),但 Safari 16.4+ 才完全支持 - 兼容方案:用
input:not(:placeholder-shown):invalid,前提是加了placeholder属性 - 更稳妥的做法是监听
input或blur,手动加touched类,再写.touched:invalid
如何避免 :focus 和 :invalid 样式打架?
直接写 input:focus:invalid { border-color: red; } 会吃掉 focus 的 outline 或 box-shadow,尤其在 Windows 高对比度模式下 outline 可能不可见。
- 分离视觉维度:用
border-color表达验证状态,用box-shadow(比outline更可靠)表达 focus 状态 - 推荐组合:
input:invalid:not(:focus) { border-color: #e53e3e; }+input:focus { box-shadow: 0 0 0 2px #3182ce; } - 别给
:invalid加transition,错误反馈需要明确即时;若整体有过渡,单独重置input:invalid { transition: none; }
type="number" 为什么输入中途就变红?
type="number" 对中间态极度敏感:空字符串、"-"、"."、甚至粘贴的非数字内容,都会立刻触发 :invalid,导致边框频繁闪烁。
立即学习“前端免费学习笔记(深入)”;
- 这不是样式问题,是 type 语义决定的校验时机
- 若需平滑输入体验,改用
type="text"+pattern或 JS 校验(例如pattern="[0-9]*") - 注意
pattern值不带斜杠:pattern="[0-9]{6}"✅,pattern="/[0-9]{6}/"❌ - 自定义校验(如用户名唯一性)必须调用
setCustomValidity(),否则:valid/:invalid完全不响应
为什么加了 required 却没反应?
:valid/:invalid 只对参与原生校验的元素生效。纯 input type="text" 即使绑了 JS,也不会被标记为 :invalid。
- 必须至少有一个校验属性:
required、type="email"、minlength、pattern、min/max -
type="phone"这类自定义 type 会被降级为text,失去所有校验能力 - 表单重置(
form.reset())会清空校验状态,但不会清除你手动加的 class,这点容易被忽略 - Safari 对动态更新支持较晚,iOS 15.4+、macOS Monterey+ 才稳定;旧版建议用 JS 状态类兜底
真正可控的“实时”反馈,几乎都得靠 JS 主动调用 checkValidity() 并同步 class;伪类适合做基础层校验视觉锚点,但别指望它扛住复杂交互逻辑。


















