max-height + transition 是移动端展开/收起最稳妥方案;卡顿或跳变主因是浏览器过渡帧计算受干扰,如 transform 破坏 scrollHeight 读取、图片未加载完导致高度误判、transition 写在 all 上引发重排、未加 will-change: max-height 致首帧延迟。

移动端点击展开/收起内容时,用 max-height + transition 是最稳妥的方案;直接写 height: auto 或依赖 display 切换,动画必然失效或跳变。
为什么 max-height 动画在移动端常卡顿或突然跳变
根本不是 CSS 写错了,而是浏览器在计算过渡帧时被干扰了。常见触发点包括:
-
transform(比如父容器用了translateZ(0)或will-change: transform)会破坏 iOS Safari 对scrollHeight的读取,导致展开后高度塌陷为 0 - 内容里有未加载完的图片或 Web Font,
scrollHeight在 JS 中读早了,值偏小,动画中途被截断 -
transition写在all上,连color、margin都一起动,触发额外重排,尤其低端安卓机明显掉帧 - 没加
will-change: max-height,浏览器来不及优化渲染管线,首帧延迟感强
如何用 JS 动态设 max-height 确保精准适配内容
静态预估(如 max-h-96)适合固定菜单,但对用户输入、API 渲染等动态内容,必须靠 JS 读真实高度。关键不在“怎么读”,而在“什么时候读”:
- 先给元素加
show类(仅含max-height: 0和overflow: hidden),再立即读offsetHeight或clientHeight强制重排 - 再读
scrollHeight,赋给style.maxHeight—— 不要跳过这一步,否则 Safari 返回 0 - 如果内容含图片,加
setTimeout(() => { /* 读 scrollHeight */ }, 10)或监听img.onload,避免高度误判 - 收起时,监听
transitionend后设style.maxHeight = '',别留内联样式影响下一次展开
overflow: hidden 必须始终生效,不能只写在收起状态
这是最容易漏掉的硬性前提:
立即学习“前端免费学习笔记(深入)”;
- 初始状态(收起)和动画中都必须有
overflow: hidden,否则内容直接溢出,动画不可见 - 不要用
visibility: hidden替代——它不占空间但不裁剪内容,max-height失去意义 - 如果内容里有
position: absolute子元素,它们会逃逸出overflow: hidden区域,需额外加contain: layout或约束父容器position: relative - 动画结束前别急着切回
overflow: visible,否则滚动条可能意外弹出,干扰用户操作
真正难的不是写几行 CSS,而是让 JS 读取时机、DOM 渲染状态、移动端渲染限制三者对齐。哪怕 scrollHeight 多读 5ms,iOS 上就可能差一个像素——这个像素,就是动画卡顿的全部原因。


















