:required伪类不触发实时校验,仅静态匹配必填元素;实时验证依赖用户交互及:valid/:invalid动态伪类,配合:placeholder-shown可精准控制错误态显示时机。

required伪类本身不触发实时校验
很多人以为给 input 加了 required 属性,浏览器就会自动在用户离开时弹红框、显示错误提示——其实不是。:required 只是个静态选择器,它只匹配带 required 属性的元素,**完全不参与表单验证逻辑**,也不监听输入、失焦或提交行为。想靠它单独实现“实时校验动效”,行不通。
真正触发校验的是用户交互(比如点击提交、调用 checkValidity()、或失去焦点后浏览器自动检查),而显示状态得靠 :valid / :invalid 这类**动态伪类**,它们会随验证结果实时切换。
-
:required适合做“统一样式初始化”,比如所有必填项默认加星号:input:required::after { content: "*"; color: red; } - 但动效(如边框变红、抖动、图标切换)必须绑定到
:invalid:not(:placeholder-shown)或:valid才能响应真实校验状态 - 注意:
:invalid在页面加载后、用户还没输任何内容时也会命中(空值默认不合法),所以常要配合:placeholder-shown排除“未开始填写”的情况
用 :invalid + :placeholder-shown 控制初始状态
刚打开页面时,所有空的 required 输入框都会被标记为 :invalid,直接加红框会显得突兀。这时候得靠 :placeholder-shown 判断用户是否“已开始操作”。
常见写法是:只在用户输入过又删空、或失焦后校验失败时才显示错误态:
立即学习“前端免费学习笔记(深入)”;
input:required:invalid:not(:placeholder-shown) {
border-color: #e53e3e;
}
input:required:valid {
border-color: #38a169;
}
-
:placeholder-shown在有 placeholder 且输入框为空时为真;用户输入任意字符后即失效 - 如果 input 没设
placeholder,:placeholder-shown永远为假,那:invalid:not(:placeholder-shown)就等价于:invalid,一加载就红——这是坑 - 稳妥做法:要么统一加 placeholder,要么改用 JS 监听
input或blur事件,手动增删class控制样式
伪元素显示验证状态要防重叠和可访问性问题
用 ::before 或 ::after 显示对勾/叉号图标很常见,但容易出两个问题:一是图标位置错乱,二是屏幕阅读器读不到状态变化。
- 绝对定位的伪元素需配合
position: relative在父input上,否则可能相对 body 定位偏移 - 图标用
content: "✓"或content: "✕"简单直接,但别用纯装饰性字体图标(比如 Font Awesome 的\f00c),它们没有语义,视障用户无法感知 - 更推荐用
aria-invalid="true"+aria-describedby关联一个隐藏的<span></span>提示文本,CSS 仅负责视觉反馈,逻辑交给 ARIA - 动画慎用:对勾/叉号的
opacity或transform过渡可以,但避免用animation: shake这类可能诱发眩晕的动效
移动端和 Safari 的兼容性细节
:placeholder-shown 在 iOS Safari 15.4+ 和 Android Chrome 57+ 支持良好,但老版本(尤其 iOS 14.x)会忽略它,导致 :invalid:not(:placeholder-shown) 始终为真。这时样式会错乱。
- 检测方式:在 Safari 开发者工具里手动删掉 placeholder 属性,看伪类是否还生效
- 降级方案:对不支持
:placeholder-shown的浏览器,用 JS 在首次input事件后给元素加data-touched="true",然后用input[data-touched="true"]:invalid替代 - 另一个坑:Safari 对
type="email"的:invalid触发非常宽松(比如输个@就标红),而 Chrome 更宽容;测试时得用真实邮箱格式校验,别只依赖伪类
真正难的不是写对那几行 CSS,而是理清「谁在什么时候改变状态」——浏览器原生验证是异步的、有延迟的,:valid 和 :invalid 的切换时机并不总和你预期一致,尤其在快速输入或粘贴时。想稳,就得接受部分逻辑交给 JS 控制。










