max-height: auto 不合法导致动画失效,应改用 scrollHeight 动态设置目标高度;收起时需监听 transitionend 后重置,动态内容须延迟读取以确保 DOM 就绪。

为什么max-height: 0 → max-height: auto 会失效
根本原因不是写错了,而是 max-height: auto 本身不合法 —— CSS 规范里 max-height 不接受 auto 值,浏览器会直接忽略该声明,退回到无过渡状态。你看到的“没动画”,其实是样式没生效,DevTools 里能看到那条 max-height: auto 被划掉。
用 JS 获取真实 scrollHeight 并注入内联样式
这是目前最稳妥、不依赖预估高度、且兼容所有现代浏览器的做法。核心是:在展开前读取内容真实高度,再设为 max-height 的目标值。
实操要点:
- 收起状态必须是
max-height: 0+overflow: hidden,不能用display: none - 展开前先强制重排(比如读
offsetHeight或调用getBoundingClientRect()),否则scrollHeight可能返回 0 - 设完
max-height后立刻触发 class 切换(如.is-open),让 transition 生效 - 收起时,先移除 class,等
transitionend事件后再把max-height设回0,否则下次展开会从 0 开始跳变
简短示例:
const el = document.querySelector('.collapsible');
el.addEventListener('click', () => {
if (el.classList.contains('is-open')) {
el.style.maxHeight = '0';
el.classList.remove('is-open');
el.addEventListener('transitionend', () => {
el.style.maxHeight = '';
}, { once: true });
} else {
el.style.maxHeight = el.scrollHeight + 'px';
el.classList.add('is-open');
}
});
scrollHeight 和 offsetHeight 的选择差异
两者都能读高度,但行为不同:
-
scrollHeight包含溢出但不可见的内容(比如有overflow: hidden时的完整内容高度),适合做展开目标值 -
offsetHeight是渲染后可见高度(含 border/padding),常用于收起后清理样式时的参考,但不能替代scrollHeight做展开 - 如果元素有 padding/border,
scrollHeight不包含它们;若需精确匹配,得手动加回来:el.scrollHeight + parseFloat(getComputedStyle(el).paddingTop) + parseFloat(getComputedStyle(el).paddingBottom)
容易被忽略的边界情况
动态内容(比如异步加载、Vue/React 渲染后)会让 scrollHeight 读早了 —— 此时 DOM 还没更新,返回的是旧高度。解决办法只有两个:
- 监听数据变化后加
requestAnimationFrame延迟一帧再读 - 对 React/Vue 组件,改用
ref的updated或useEffect生命周期钩子,在 DOM 确认就绪后再触发高度测量
另外,transitionend 事件只在 max-height 变化时触发,如果内容高度为 0(空节点),动画不会启动,需要单独判断并跳过 transition 流程。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











