height: 0 → height: auto 无法动效因auto非数值,浏览器无法插值;应改用max-height过渡,配overflow: hidden、opacity和visibility控制显隐,JS仅操作class触发CSS动画。

为什么 height: 0 → height: auto 不会动效
浏览器无法对 height: auto 做数值插值——transition 需要明确的起始和结束像素值才能逐帧计算中间状态。auto 是一个计算指令,不是具体数值,所以动画直接跳变或完全不触发。
常见错误现象:transition: height 0.3s + height: 0 / height: auto,结果要么瞬间闪现,要么卡在初始态不动。
- 别写
height: auto作为过渡终点 - 也别用
display: none切换——它不可动画,且会中断正在播放的 transition - 更别在 JS 里直接写
el.style.height = '200px',这会覆盖 CSS 中定义的 transition 规则
用 max-height 实现真正可用的平滑过渡
这是目前最轻量、零 JS、兼容性最好、且无需测量真实高度的方案。核心是借 max-height 的可动画性 + overflow: hidden 隐藏溢出内容。
初始状态应设为:max-height: 0 + overflow: hidden + opacity: 0 + visibility: hidden;展开状态设为:max-height: 500px(或更大)+ opacity: 1 + visibility: visible。
立即学习“前端免费学习笔记(深入)”;
-
max-height值不是“无限”,而是“足够大”:需明显大于内容最大可能高度(含padding、line-height、字体缩放等),但别设成100vh——Safari 下地址栏显示/隐藏会导致vh波动,500px或1000px更稳 - 只对
max-height和opacity写transition,visibility不参与过渡(它只能立即切换) - 避免写
transition: all 0.3s:万一菜单里有border-color或font-size变化,会触发无关动画,拖慢渲染
JS 控制时只操作 class,不碰 style
哪怕你用 JS 触发展开,也绝不要手动设置内联样式。所有动画逻辑必须留在 CSS 里,JS 只负责状态切换。
正确做法:el.classList.toggle('is-open'),然后靠 CSS 类名控制显隐和过渡。
- 如果内容是异步加载的(比如 API 返回后插入 DOM),要在内容渲染完成后再添加
is-open类,否则max-height过渡起点仍是 0,收起时会“抽搐” - 若需精确匹配真实高度(比如超长文本),可 JS 读取
el.scrollHeight后内联设置style.maxHeight = el.scrollHeight + 'px',再加类触发动画——但这属于进阶混合方案,多数场景max-height: 500px已足够 - 纯 CSS 方案可用
<input type="checkbox" id="toggle">+<label for="toggle">模拟开关,注意隐藏 checkbox 要用position: absolute; opacity: 0; width: 1px; height: 1px;,不能用display: none(会破坏可访问性)
cubic-bezier() 比时长更影响体验
0.3s 是合理时长,但缓动函数选错,用户会觉得“卡”或“拖”。默认 ease 开头太慢、结尾太急;linear 全程匀速,缺乏自然感。
推荐用 cubic-bezier(0.25, 0.46, 0.45, 0.94):类似 ease-out,但起始响应更快,收尾更柔和。
- 别用
ease-in:开头延迟明显,用户点完没反馈,容易误以为没生效 - 移动端点击热区至少 44×44px,给
<label>加cursor: pointer和足够padding - 键盘用户按 Tab 进入时,记得处理
:focus-within或:focus-visible,否则可访问性就断了
scrollHeight,比拍脑袋填个 500px 更可靠。


















