不能直接用 :invalid 伪类,因其仅在字段“脏了”且校验失败时生效,空 required 字段初始为 :valid,无法满足空字段显灰边框+提示的设计需求;移动端 safari 对 :user-invalid 支持差;:invalid 无法控制容器、图标、错误文案等周边元素;仅返回布尔值,无法区分多规则错误类型;无障碍支持弱,不自动关联 aria-describedby。

为什么不能直接用 :invalid 伪类标记非法输入
浏览器原生的 :invalid 只在字段“脏了”(touched)且校验失败时才生效,空的 required 字段初始就是 :valid,哪怕它根本没输——这和设计要求的“空字段显灰边框+提示文字”冲突。移动端 Safari 对 :user-invalid 支持极差,纯 CSS 方案在关键路径上不可靠。
更麻烦的是::invalid 只能作用于 input 元素本身,无法控制包裹容器、图标、错误文案等周边元素。你没法用它让整个 field-email 块变红、显示图标、拉出错误消息。
-
:invalid是被动响应,而验证反馈必须主动可控:用户点提交前就要有状态,JS 校验结果一出来就得立刻更新 UI - 多个规则共存(空 + 格式错 + 域名白名单不通过)时,
:invalid只返回布尔值,无法区分具体错误类型 - 无障碍支持弱:
:invalid不自动关联aria-describedby,屏幕阅读器读不出错在哪
field-email--invalid 这类修饰符该挂在哪一层
必须挂在字段级 block 的根容器上,比如 <div class="field-email field-email--invalid">,而不是 <code>input 或整个 form。
结构示例:
<div class="field-email field-email--invalid"> <label for="field-email__input" class="field-email__label">邮箱</label> <input id="field-email__input" class="field-email__input"><div class="field-email__error">请输入正确的邮箱地址</div> </div>
- 样式通过后代选择器下发:
.field-email--invalid .field-email__input控边框,.field-email--invalid .field-email__error控显隐 - JS 只需操作一个 class:
fieldEl.classList.toggle('field-email--invalid', !isValid),干净、可预测、易调试 - 避免把修饰符挂到
form上——否则一个字段错,整表单变红,语义错乱,用户无法定位问题 - 禁止写成
field-email__input--invalid:输入框可能复用于搜索等非表单场景,此时修饰符语义失效
多个验证状态共存时,修饰符怎么命名才不爆炸
别堆砌 --empty --format --domain,CSS 权重会失控,维护时改一个就可能意外影响另一个。BEM 修饰符不是日志,它是契约:一个字段在同一时刻只有一个“对外可见的状态”。
- 推荐统一用
field-email--invalid表达“当前不合法”,具体错误文案由.field-email__error的文本内容决定,而非 class 名 - 若需区分状态粒度(比如空值 vs 格式错),可用
field-email--empty/field-email--format,但前提是你的 JS 校验函数明确返回这些字符串,并且 CSS 规则互斥、无重叠 - 绝对不要写
field-email--invalid--empty:双修饰符是非法 BEM,它不再是修饰符,而是新块名,破坏可读性和工具链支持 - 禁用
js-前缀,如field-email--js-invalid:CSS 不该依赖 JS 实现细节,状态驱动应来自业务逻辑,不是实现方式
容易被忽略的 DOM 和无障碍细节
修饰符只是 class 名,真正起作用的是它是否真实存在于 DOM 中,以及是否与 HTML 结构对齐。很多“状态不生效”问题,根源不在 BEM,而在 DOM 绑定断裂。
-
for和id必须严格匹配:<label for="field-email__input"></label>对应<input id="field-email__input">,ID 不能动态生成后忘了同步更新 - 构建工具(如 PurgeCSS)可能误删动态添加的类,需在 safelist 中显式保留
/field-email--(invalid|empty|required)/ -
field-email--invalid必须同步设置aria-invalid="true"和aria-describedby="field-email__error",否则屏幕阅读器无法感知 - React/Vue 中,确保 state 更新确实触发了 className 重渲染,不是只改了
isValid却漏映射到 class 列表











