transition: display 从未生效,因display是离散属性无中间状态,浏览器静默丢弃该声明;应改用opacity+visibility协同控制,显式声明两者transition并用transitionend精准清理。

transition: display 从来就没生效过——不是你写错了,是浏览器压根不处理,整条声明被静默丢弃。
为什么 transition: display 完全没反应
display 是离散属性,取值只有 none、block、flex 等枚举值,没有“50% 显示”这种中间状态可插值。浏览器在解析时直接跳过 transition: display 0.3s 这类规则,DevTools 的 Computed 面板里根本找不到它。更关键的是:display: none 会让元素立刻脱离渲染树,连子元素的 opacity 动画都会一并终止。
opacity + visibility 组合必须显式声明且错开时机
这不是简单换两个属性,而是分工明确的协同控制:
- 初始态必须同时设
opacity: 0和visibility: hidden,只靠opacity隐藏会挡点击、抢焦点 -
transition必须显式列出两者:transition: opacity 0.25s ease, visibility 0s 0.25s(visibility延迟到opacity动画结束才切换) - 别用
transition: all——它不会自动包含visibility,必须手写 - 显示类中要同步设
opacity: 1和visibility: visible,否则元素不可交互
动画结束后要不要补 display: none
取决于是否需要释放布局空间或满足可访问性要求:
- 如果只是淡入淡出、下拉菜单这类场景,
visibility+opacity已足够,不用动display - 如果内容庞大(如整页弹窗),或父容器有
overflow: hidden导致占位撑开滚动条,就必须在动画结束后补display: none - 监听
transitionend事件,且只在event.propertyName === 'opacity'时执行清理,避免误触发 - 同步设置
aria-hidden="true"并移除tabindex或 blur 当前焦点,否则键盘用户可能卡在不可见元素上
JS 同步操作 display 后加 class 为什么还是跳变
浏览器会把 el.style.display = 'block' 和 el.classList.add('fade-in') 合并为一次渲染,起始帧直接被跳过。常见于第三方弹窗、下拉菜单等封装组件中。
- 必须打断渲染队列:先设
display: block,再读一个触发 layout 的属性(如el.offsetHeight),最后加 class - 不能用
getComputedStyle代替offsetHeight——后者才强制重排,前者不一定触发 - 高频调用(比如滚动中反复显示)会引发 layout thrashing,导致卡顿,慎用
真正容易被忽略的是 DOM 卸载时机与动画完成的错位。哪怕 CSS 写得再准,只要没在 transitionend 里检查 propertyName 并精准清理,就可能留下占位、焦点陷阱或可访问性漏洞。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











