label 不支持 :invalid 伪类,因其无验证逻辑;需用 css 组合选择器(如 .field input:invalid ~ label)或 js 控制 class/aria-invalid 来响应错误状态。

label 本身不感知 invalid 状态,必须靠 CSS 选择器联动或 JS 手动控制才能让 label 配合错误提示 —— 直接写 label:invalid 是无效的,浏览器根本不支持这个伪类。
为什么 label:invalid 写了也没用
label 元素没有内置验证逻辑,:invalid 伪类只匹配 <input>、<select></select>、<textarea></textarea> 这类可验证表单控件。label 是旁观者,不是参与者。
常见错误写法:label:invalid { color: red; } → 样式永远不会生效。
- 想让 label 响应输入框错误,得用组合选择器,比如
input:invalid + label(要求 label 紧跟 input 后)或.field input:invalid ~ label(需包裹容器) - 更可靠的方式是把 label 放在 input 前面,用
input:invalid + label不成立,改用.field input:invalid + label也不行——顺序错就定位失败 - 真正能稳定工作的结构是:label 和 input 同级、label 在前、共包一层
<div class="field">,再用 <code>.field input:invalid ~ label或 JS 控制label的 classinput:invalid + label为什么经常失效这个相邻兄弟选择器看似简单,但对 DOM 顺序极其敏感。只要中间插了注释、空格文本节点、或别的元素(比如
<span class="hint"></span>),链路就断了。- 正确结构(无干扰节点):
<input><label>邮箱</label> - 错误结构(含换行/空格):
<input>\n <label>邮箱</label>→ 换行符生成 Text 节点,破坏“相邻”关系 - 错误结构(中间有元素):
<input><span class="icon"></span><label>邮箱</label>→+只认紧挨着的下一个同级元素 - 移动端或 SSR 渲染时,自动插入的空白节点更难肉眼察觉,建议用 DevTools 的“Show whitespace”功能排查
用 JS 让 label 准确响应
:invalid状态纯 CSS 组合选择器受限太多,尤其当表单结构动态、label 位置不固定、或需要支持
setCustomValidity()自定义校验时,JS 是唯一可控方案。- 监听
invalid事件:它在浏览器内部校验失败时触发,但**不冒泡**,只能绑定到具体 input 上 - 推荐做法:在
submit事件里统一处理,调用form.checkValidity()后遍历所有字段,根据input.validity.valid设置 label 的 class 或aria-invalid - 别只依赖
input:invalid的初始状态:空required字段一进页面就是:invalid,但用户可能还没开始填,过早标红反而干扰;建议用:user-invalid(Chrome 102+、Edge 102+)或手动加.touchedclass 控制时机 - label 上要同步更新
aria-invalid="true"和aria-describedby,否则屏幕阅读器读不到错误关联
label 浮动提示语时容易忽略的可访问性细节
把 label 拉到输入框上方显示错误文案,视觉上很清爽,但若处理不当,会直接切断辅助技术链路。
- 浮动后的 label 必须仍保留在 input 的 DOM 前方(不能用
transform或margin-top把它“视觉上拉上去”,而物理位置还在下面) - label 里写的错误文案,不能是纯装饰性文字;要确保它和 input 的
id通过aria-describedby显式关联,且该文案元素需带role="alert"实现实时播报 - 别用
::after伪元素生成错误提示 —— 屏幕阅读器默认不读伪元素内容 - 如果 label 浮动后遮住了 input 的 focus outline,不要靠
z-index强顶;检查是否意外给 input 设了position: relative或z-index,这会创建新层叠上下文,让 label 失去参照
最麻烦的不是怎么让 label 动起来,而是怎么让它动得既好看、又说得清、还被所有人听见 —— 尤其当表单混用原生校验、自定义规则、多语言切换时,label 的状态管理很容易变成三不管地带。
- 正确结构(无干扰节点):











