不该同时存在form-field--invalid和form-field--valid,因验证状态互斥且form-field--valid易致语义误判;应仅用form-field--invalid主动反馈错误,初始态靠默认样式或form-field--touched区分,确保状态单一、可维护、无障碍同步。

form-field--invalid 和 form-field--valid 该不该同时存在
不该。验证状态是互斥的,一个字段同一时刻只能处于“有效”“无效”“未校验”三种状态之一,但 form-field--valid 容易引发误判:用户刚输入一个合法邮箱,JS 立即加了这个类,但此时表单还没提交、其他字段仍是空的——整个业务逻辑上它并不“有效”。更稳妥的做法是只定义 form-field--invalid,用默认样式表达“初始态/未触发校验”,用 form-field--valid 反而会让开发者陷入“什么时候才算真正 valid”的语义纠缠。
常见错误现象:form-field--valid 被用于视觉反馈(比如绿色边框),结果用户输完立刻变绿,但提交时因后端校验失败又跳红,造成信任错乱;或者空字段在没获焦前就显示绿色,违背表单交互直觉。
- 只保留
form-field--invalid作为主动反馈类,由 JS 校验函数返回false时添加,返回true或undefined时移除 - “成功态”用
form-field--touched+form-field--valid组合控制(仅限明确需要视觉确认的场景,如密码强度实时提示) - 初始态不依赖任何修饰符,靠
.form-field__input:not(:placeholder-shown)或.form-field--touched区分是否已输入
为什么不能用 form__input--error 这种写法
因为 form__input--error 把状态挂到了元素层级,破坏了 BEM 的封装边界。输入框(input)本身不是可独立复用的功能单元,它必须和 label、错误文案、校验逻辑一起构成一个完整字段块(form-field)。一旦你把 --error 加在 input 上,样式就只能作用于那个 input,无法统一控制 label 颜色、错误消息显隐、icon 状态等关联行为。
使用场景:登录页要让“邮箱错误”时 label 变红、input 边框变红、下方显示“请检查邮箱格式”,这三者必须同步响应同一个状态信号——只有把修饰符放在容器 form-field 上,才能用 CSS 写出 .form-field--invalid .form-field__label、.form-field--invalid .form-field__input 这样清晰可控的选择器。
-
form__input--error违反 BEM 原则:Element(__input)不能带 Modifier(--error),Modifier 只能属于 Block 或 Element 本身,不能“跨层”描述组合状态 - 复用困难:同一个
input元素可能被塞进搜索框、过滤器、表单里,--error在非表单场景下语义失效 - JS 控制成本高:每次状态变更要操作多个 class(input、label、message),不如只操作一个根节点 class 干净
aria-invalid 和 class 同步更新的关键点
不能只改 class 不动 aria-invalid,也不能反过来。屏幕阅读器依赖 aria-invalid="true" 主动播报错误,而视觉样式依赖 class 控制颜色/边框/显隐。两者必须严格同步,否则会制造可访问性断层。
性能影响小,但逻辑断点常出现在 JS 异步校验后忘记更新属性,或手动移除 class 时漏掉 aria-invalid。最简做法是把这两件事封装进同一个函数:
- 校验失败时:同时执行
fieldEl.classList.add('form-field--invalid')和inputEl.setAttribute('aria-invalid', 'true') - 校验通过或重置时:同时执行
fieldEl.classList.remove('form-field--invalid')和inputEl.removeAttribute('aria-invalid') - 避免用
aria-invalid="false"—— WAI-ARIA 规范明确指出,false和缺失该属性效果相同,且部分读屏器对false解析不稳定
多个规则共存时,怎么避免 --invalid 和 --required 冲突
别并列使用 form-field--invalid form-field--required。修饰符不是开关集合,而是最终决策结果。JS 校验函数应返回一个单一布尔值(是否当前字段整体无效),而不是分别判断“是否为空”“是否邮箱格式”“是否长度不足”再各自加类。
容易踩的坑:写成 if (!value) el.classList.add('form-field--required'); + if (!emailRegex.test(value)) el.classList.add('form-field--invalid');,结果两个类同时存在,CSS 里又写了 .form-field--required .form-field__input 和 .form-field--invalid .form-field__input,权重一模一样,谁在后面谁生效,维护时极易失控。
- 所有规则收敛到一个校验函数,例如
validateEmailField(value),返回true/false/null(未校验) - 只根据返回值决定是否加
form-field--invalid,不拆解中间原因 - 具体错误文案由 JS 注入
.form-field__message,而不是靠不同修饰符切换不同提示
真正复杂的地方在于:校验时机(失焦?输入中?提交时?)和错误聚合策略(只报第一个错,还是全量展示),这些没法靠 CSS 修饰符解决,得在 JS 层设计清楚。BEM 修饰符只是把最终确定的状态“画出来”,它不参与决策。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











