display: none 无法触发过渡动画,因它是 layout 级属性、无中间状态;须分三步:先设 display+opacity:0/visibility:hidden,再强制重排后加过渡类,最后在 transitionend 中清理 display 和 aria-hidden。

直接用 display: none 切换元素,过渡动画必然断开——这不是写法问题,是浏览器渲染机制决定的:display 属于 layout 级别属性,一设即走,没有中间状态,transition 完全不生效。
核心矛盾在哪
痛点不在“怎么动”,而在“怎么衔”。常见错误是:
– 先 JS 设置 el.style.display = 'block',再加 show 类触发 opacity 过渡;
– 或反过来,先加类、后改 display。
这两种都让浏览器把两步合并为一次重绘,起始帧直接跳过,动画“啪”一下就出现或消失。
必须分三步走:显式断开渲染队列
让浏览器明确区分“占位准备”“视觉过渡”“空间释放”三个阶段:
-
第一步(准备):设
display: block(或 inline/table 等所需值),同时设opacity: 0; visibility: hidden; -
第二步(触发):强制浏览器重排,再加过渡类。推荐写法:
el.offsetHeight;(读取布局触发重排)el.classList.add('fade-in'); -
第三步(收尾):监听
transitionend,仅当event.propertyName === 'opacity'时,才设display: none并同步aria-hidden="true"
CSS 写法要精准控制节奏
不能只靠 transition: all,visibility 本身不可动画,但可以靠 transition-delay 协调时机:
- 入场类:
opacity: 1; visibility: visible; transition: opacity 0.25s ease-in, visibility 0s; - 退场类:
opacity: 0; visibility: hidden; transition: opacity 0.25s ease-out, visibility 0s 0.25s; - 这样 visibility 在 opacity 动画快结束时才切换,既防误点,又避免半透明状态下交互异常
要不要真设 display: none?看场景
不是所有情况都需要它:
- 下拉菜单、Tab 面板、提示气泡:用
visibility: hidden + opacity: 0足够,保留 DOM 占位反而利于滚动稳定和焦点管理 - 整页弹窗、富文本编辑器浮层、大图预览:必须在 opacity 动画结束后补
display: none,否则撑开父容器、干扰 overflow 或引发滚动条抖动 - 若需彻底释放空间且高度可预估,可叠加
max-height过渡(如max-height: 0 → 500px),并配overflow: hidden











