max-height 是唯一靠谱的替代方案,因浏览器无法对 height: auto 做数值插值,而 max-height 可动画且需配合 overflow: hidden;动态设值应读取 scrollHeight,并在 transitionend 后清空样式。

height: 0 → height: auto 加 transition 一定不生效,这是浏览器限制,不是你代码写错了。
为什么 max-height 是唯一靠谱的替代方案
浏览器无法对 auto 做数值插值,而 max-height 是可动画属性,只要起止都是具体像素值(比如 0px → 500px),就能逐帧计算过渡。关键还得配 overflow: hidden,否则内容会撑开容器、破坏动画观感。
- 初始状态必须是
max-height: 0+overflow: hidden,缺一不可;否则 transition 不触发 - 展开时不能直接写死
max-height: 500px—— 内容高度不确定时,设小了截断,设大了收起拖尾 - 真实项目中应动态读取
element.scrollHeight:展开前赋值menu.style.maxHeight = menu.scrollHeight + 'px' - 收起时不能立刻清空
maxHeight,得先设回'0px',再监听transitionend事件,检查event.propertyName === 'max-height'后才清空内联样式
用 transform: scaleY() 的适用场景和坑点
当菜单结构简单、不需要精确高度匹配,且要避开 max-height 动态计算逻辑时,transform: scaleY() 是更轻量的选择。它不依赖内容高度估算,性能也更好(走合成层)。
- 初始状态:
transform: scaleY(0)+transform-origin: top+overflow: hidden - 展开状态:
transform: scaleY(1) - 缩放会轻微挤压文字,iOS Safari 或高 DPI 屏上可能字体渲染发虚,需实测
- 如果菜单里有
position: absolute子元素,它们可能在缩放过程中错位,因为 transform 会影响包含块
JS 控制时最容易踩的三个雷
动画逻辑写在 CSS 里,JS 只负责开关 class,这是底线。一旦 JS 直接操作 style.height 或 style.maxHeight,transition 就会被覆盖失效。
立即学习“前端免费学习笔记(深入)”;
- 永远用
el.classList.toggle('is-open'),而不是el.style.height = '200px' - 移动端点击后焦点容易丢失,导致
:focus-within失效 —— 收起逻辑必须监听整个下拉区域(包括所有子项)的blur,不能只靠父容器 - 键盘弹出后立即读
scrollHeight会不准,建议加setTimeout(..., 100)或等resize事件稳定后再读
真正卡住人的从来不是“怎么让菜单动起来”,而是 max-height 值是否贴合真实内容、transitionend 是否漏触发、以及焦点在 iOS 和安卓 WebView 里是否还连得上 —— 这三件事没串牢,动画就只是看起来能动而已。


















