height: auto无法过渡因浏览器无法插值计算,必须用max-height配合CSS变量与JS动态注入scrollHeight真实高度,并设置fallback值、强制重排及同步opacity过渡以确保平滑精准。

不能用 height: auto 做过渡动画,浏览器压根不认这个值——它不是数字,没法插值。必须用 max-height + CSS 变量配合 JS 动态注入真实高度,才能既平滑又精准。
为什么 height: auto 配 transition 完全无效
你写了 transition: height 0.3s,点开菜单却“啪”一下弹出来,收起时也没动画。这不是写错了,是浏览器直接放弃过渡:因为 height: auto 没有像素值,起始帧和结束帧之间没有可计算的差值路径。控制台不会报错,但动画就是不跑。
常见错误写法:
-
height: 0; transition: height 0.3s;→ 展开时设height: auto,结果无动画 -
max-height: 0; transition: max-height 0.3s;→ 展开时设死max-height: 1000px,内容少时拖尾明显,内容多时被截断
如何用 scrollHeight + CSS 变量动态设 max-height
核心是让 JS 算出真实高度,存进变量,CSS 读取并触发过渡。但顺序和时机不对,就会闪屏或动画从 0→0 开始。
立即学习“前端免费学习笔记(深入)”;
- 先确保元素已渲染且可见:若初始
display: none或max-height: 0+overflow: hidden,scrollHeight会返回0 - 稳妥做法:临时设
visibility: hidden+display: block(不占布局流),再读el.scrollHeight,之后立刻恢复原样式 - 读完必须强制重排:
void el.offsetHeight,否则浏览器可能缓存旧值,变量更新了但动画没触发 - CSS 中写
max-height: var(--target-height, 400px),fallback 值不能省——Safari 对var(--x)单独使用时 fallback 不稳定
max-height 过渡中 overflow: hidden 和 opacity 怎么配
只靠 max-height 过渡,内容在收缩末尾容易“撕裂”:文字被硬截断、半截字悬在容器底边。这不是 bug,是 overflow 截断和字体渲染的时序差。
- 必须加
overflow: hidden,否则内容会撑破容器边界 - 推荐同步加
opacity过渡:opacity: 0初始态,opacity: 1展开态,和max-height同步transition - 别写
transition: all 0.3s,显式声明transition: max-height 0.3s ease, opacity 0.3s ease,避免其他属性干扰 - 收起动画结束后,记得加
inert属性:el.setAttribute('inert', ''),防止键盘焦点误入不可见区域
媒体查询与变量协同控制响应式折叠状态
纯靠 JS 切换变量不够鲁棒。小屏应默认折叠,桌面端默认展开,变量只是微调开关,不是数据搬运工。
- 在
:root声明基础变量:--menu-height: 0、--menu-open: 0 - 用媒体查询覆盖:
@media (min-width: 768px) { :root { --menu-height: 100vh; --menu-open: 1; } } - JS 只负责切换
--menu-open,CSS 用max-height: calc(var(--menu-open) * var(--menu-height))控制显隐(注意单位一致,100vh × 1有效,0 × anything = 0) - 别在
calc()里嵌套复杂表达式,比如calc(var(--menu-height) + 10px)—— Safari 对这类 calc 内变量动画支持极不稳定
真正难的不是第一次展开有动画,而是当内容含图片、iframe、异步加载项,或者父容器有 transform 导致层叠上下文变化时,scrollHeight 计算失准、overflow: hidden 失效、动画卡顿——这些没法靠一个通用方案兜底,得逐 case 查 DOM 渲染状态和 computed style。


















