max-height过渡动画需动态匹配内容高度:展开时设为scrollHeight,收起时先置0再监听transitionend清空;须避开移动端焦点丢失、键盘弹出干扰及系统禁用动画问题。

直接用 height: auto 做 transition 会失效——浏览器无法计算 auto 的终点值,动画会跳变或卡住。必须换用可插值的属性,比如 max-height 或 transform。
为什么 max-height 过渡常“拖尾”或截断内容
设死 max-height: 500px 看似简单,但实际高度不固定时:设小了菜单被裁剪;设大了收起动画明显拖沓(比如从 500px → 0 耗时固定,哪怕内容只高 60px)。这不是 CSS 写错了,是值没对上真实内容高度。
- 真实高度应由
scrollHeight动态读取,不是靠猜 - 初始状态必须是
max-height: 0+overflow: hidden,否则过渡不触发 - 展开时赋值
element.style.maxHeight = element.scrollHeight + 'px' - 收起时不能立刻清空样式,得先设回具体值,再监听
transitionend清空,否则跳变
transitionend 监听比 setTimeout 更可靠
用 setTimeout 延迟清空 maxHeight,一旦用户快速连点、动画被中断,残留的内联样式会让下一次展开失败。而 transitionend 是浏览器原生事件,只在动画真正结束时触发。
- 收起前先设
menu.style.maxHeight = '0px' - 监听
transitionend,检查event.propertyName === 'max-height'再清空:menu.style.maxHeight = '' - 避免监听所有 transitionend,防止其他属性干扰
移动端和键盘焦点下的常见断裂点
iOS Safari 或部分安卓 WebView 中,点击空白处收不起菜单,往往不是 JS 逻辑错,而是焦点离开触发器后,焦点还没落到菜单项就丢失了——:focus-within 在这些环境里支持不稳定,且键盘弹出可能把菜单顶出视口,导致 scrollHeight 读错。
立即学习“前端免费学习笔记(深入)”;
- 别依赖纯 CSS 的
:hover或:focus控制展开/收起,尤其在触屏场景 - 收起逻辑要覆盖「焦点彻底离开整个下拉区域」,包括菜单自身和所有子项
- 键盘弹出后建议延迟 100ms 再读
scrollHeight,避开布局未稳定阶段 -
prefers-reduced-motion查询必须加,否则动画在系统设置关闭动画时仍强制执行
真正难的不是写出动画,而是让 max-height 始终贴合内容、让 transitionend 不漏触发、让焦点流转在各种设备上不掉链子——这三件事串起来,才叫可用的下拉动画。


















