根本原因是浏览器只能对确定像素值做线性插值,max-height: 0→500px若内容实际高520px则末尾被截断;须用足够大但安全的固定值(如500px)、始终保留overflow-hidden,并避免auto、vh及过大数值。

max-height 动画失效的根源是值不匹配真实高度
浏览器对 max-height 的过渡是线性插值计算,起点和终点都必须是确定像素值。设成 max-height: 0 → max-height: 500px,动画就按 0→500 做匀速变化;但若内容实际高 520px,末尾 20px 就被 overflow: hidden 硬切掉——这不是动画问题,是尺寸错配。
盒模型干扰会让预估高度失准
真实高度 = 内容行高 × 行数 + 所有 padding + border + 子元素 margin(若未折叠)。常见翻车点:
-
padding-top: 16px和padding-bottom: 16px多算 32px - 子列表项带
margin-bottom: 8px,5 项就多出 32px(最后一项不该有) - 字体加载延迟导致初始
scrollHeight读小(尤其 Web Font) - 父容器用了
transform,会创建新层叠上下文,使overflow: hidden对子元素失效
为什么不能直接用 scrollHeight 做 JS 动态设置
不是不能,而是时机不对。常见错误写法:
button.addEventListener('click', () => {
el.style.maxHeight = el.scrollHeight + 'px'; // ❌ 同步设值,浏览器来不及重排
});
正确节奏必须分三步:
立即学习“前端免费学习笔记(深入)”;
- 先设
el.style.maxHeight = '0' - 强制重排:
el.offsetHeight(读取一次触发 layout) - 再设
el.style.maxHeight = el.scrollHeight + 'px'
移动端和 Safari 的隐藏陷阱
某些旧版 Safari 对过大的 max-height(如 > 2000px)插值计算极慢,动画末尾明显拖沓;而 Android WebView 在 vh 单位缩放时会突变截断。稳妥做法:
- 静态内容:用 Sass 预设语义化高度变量,如
$panel-height-md: 280px - 动态内容:上限控制在
max-height: 120vh或60rem,避免9999px - 永远显式声明
transition: max-height 0.3s ease, opacity 0.3s ease,禁用transition: all
真正难的不是算出一个数,而是当面板里塞进图片、iframe、flex 子项或响应式换行后,scrollHeight 可能比渲染完成时小 1–2 像素——这点误差在动画末帧就会暴露为文字压扁或半截悬空。


















