不能用js直接操作display或visibility,必须用bem修饰符(如--step-active、--step-hidden)控制显隐,确保状态与样式一致、支持动画、媒体查询及可访问性,js仅增删类名,css只写修饰符规则。

多步骤表单的显示隐藏不能靠 JS 直接操作 display 或 visibility,必须用 BEM 修饰符(--step-active、--step-hidden)驱动样式,否则状态与样式脱节,调试时找不到控制源头。
为什么不能在 JS 里写 el.style.display = 'none'
这种写法会绕过 BEM 的语义契约:样式不再反映组件真实状态,DevTools 里看不到 form-step--hidden 类,grep 搜不到控制逻辑;更严重的是,CSS 动画、媒体查询、伪类(如 :focus-within)全失效。BEM 要求“状态即类名”,所有视觉变化必须由类名触发。
- JS 只负责增删类名:
stepEl.classList.add('form-step--hidden') - CSS 中只写修饰符规则:
.form-step--hidden { display: none; },不写任何后代选择器 - 避免用
form-step__content这类元素类控制显隐——它属于结构,不该承载状态
form-step--active 和 form-step--hidden 怎么分工
两者不是互斥开关,而是正交状态:一个表单步骤可以同时是 --active(当前焦点)和 --valid(校验通过),但不能同时是 --active 和 --hidden。关键在于语义清晰:
-
form-step--active:仅用于高亮当前步骤、启用其内表单控件、允许键盘导航 -
form-step--hidden:纯视觉隐藏,不影响 DOM 结构和可访问性(仍保留在 tab 顺序中) - 禁用某步但保留占位?用
form-step--disabled+pointer-events: none,而非隐藏
跨步骤共享状态怎么避免命名污染
比如“手机号已验证”需要影响步骤 2(输入)和步骤 3(确认),不能在 step-2__phone 和 step-3__phone 上分别加修饰符。正确做法是升格为独立 Block:
- 抽离
phone-verify-status作为顶层状态块,含phone-verify-status--verified修饰符 - 各步骤内部通过
form-step__content的子容器引用它:<div class="form-step__content"><div class="phone-verify-status"></div></div> - CSS 中禁止写
.form-step--step2 .phone-verify-status——这引入隐式依赖,违背 BEM “样式只属于一个 Block” 原则
动画切换时容易漏掉的关键点
用 transition: opacity .2s, transform .2s 做淡入滑动效果很常见,但实际踩坑最多的是:没有重置 transform 的初始值,导致上一步残留平移。BEM 要求每个修饰符的样式必须完整、自包含:
-
.form-step--hidden必须显式写transform: translateX(-20px); opacity: 0;,不能只写opacity: 0 -
.form-step--active必须显式写transform: translateX(0); opacity: 1;,不能依赖默认值 - 禁用
will-change: transform在非必要步骤上——它会强制创建图层,滚动时可能闪屏
最常被忽略的是可访问性:用 display: none 隐藏的步骤,屏幕阅读器会跳过;若需保留语义流,改用 visibility: hidden + position: absolute 移出视口,再配合 aria-hidden="true" 显式声明。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











