:invalid等原生表单伪类需同时满足四个条件才生效:HTML含required/pattern等验证属性、用户已交互(输入/失焦/提交)、选择器排除placeholder-shown状态、构建工具正确处理伪类。

原生表单验证状态(如 :invalid、:valid、:user-invalid)在 Less 中写出来却没反应,不是编译失败,而是浏览器压根没触发状态匹配——你得同时满足 HTML 属性、用户交互、选择器结构、构建配置四个条件,缺一不可。
为什么 :invalid 写了但完全不生效
浏览器只在两个前提都成立时才真正激活 :invalid:
-
input必须带required或pattern等原生验证属性(光靠 JS 设置setCustomValidity('')不够) - 用户必须已与该控件发生交互:输入过、失焦过、或提交过表单(刚加载的空
input[required]实际是:valid) - 别用
input:invalid这种宽泛写法——placeholder 显示时它会被当成:valid;改用input:invalid:not(:placeholder-shown) - 旧版 Safari(:focus:invalid,得降级为
:user-invalid(Chrome 102+ / Firefox 119+ 已支持)
Less 嵌套中伪类顺序和 & 绑定的坑
嵌套时 & 只绑定上一级选择器,中间插类名会破坏伪类紧贴元素名的语法要求:
- 错误写法:
.form-group input { &:focus:invalid { } }→ 编译成.form-group input:focus:invalid,权重升高且易被覆盖 - 正确写法:
input[required]:focus:invalid或input[required]:user-invalid,语义明确、浏览器兼容性高 - 别在 mixin 外层或变量块里写
&:focus:invalid——&没绑定目标,会产出无元素名的:focus:invalid,浏览器直接丢弃 -
:hover和:active顺序不能错:例如a:active:hover非法,a:hover:active才合法
用参数化 mixin 封装状态样式,避免重复写死
把验证状态逻辑抽成带参数的 mixin,既能复用,又能保持选择器语义清晰:
立即学习“前端免费学习笔记(深入)”;
.form-validation-states(@selector) {
@{selector}:valid:not(:placeholder-shown) {
border-color: #28a745;
}
@{selector}:invalid:not(:placeholder-shown) {
border-color: #dc3545;
}
@{selector}:user-invalid {
border-color: #dc3545;
}
}
调用时传入具体选择器片段:
.form-validation-states('input[type="email"]');.form-validation-states('textarea');- 别传变量名(如
@btn),必须传字符串字面量(带引号)
构建工具删掉你的 :invalid 规则?关掉 discardUnused
Less 本身不压缩 CSS,真正动手的是 cssnano 或 clean-css。它们默认开启 discardUnused,会把未显式使用的状态规则(比如只在动态 JS 场景下才触发的 :invalid)判定为“冗余”并删除:
- 在
postcss.config.js中显式关闭:discardUnused: false和normalizeWhitespace: false - 别用
cssnano/preset-default简写——它隐含启用这些危险选项 - Vue 的
scoped样式也会干扰原生伪类匹配,需用:deep(input:invalid)或显式命名空间(如.form-wrapper input:invalid)穿透
最常被忽略的一点:状态样式是否生效,80% 取决于 HTML 是否真写了 required,而不是 Less 写得有多漂亮;剩下 20% 是构建链路悄悄删掉了你写的规则——先查 HTML,再查构建配置,最后再翻 Less 嵌套逻辑。


















