应使用 flex-basis 替代 height 实现手风琴动画:父容器设 display: flex; flex-direction: column,子项用 transition: flex-basis 0.3s ease,并通过 JavaScript 动态设置具体像素值。

手风琴展开时高度突变,transition不生效怎么办
直接对 height 做 transition 会失效——因为从 0 到 auto 无法插值。常见错误是写成 transition: height 0.3s ease,但浏览器根本不会动画。
真正可靠的做法是用 flex-basis 替代 height:当容器是 display: flex; flex-direction: column 时,子项的 flex-basis 可被过渡,且它控制的是“主轴尺寸”,在垂直布局中就等效于高度。
- 父容器必须设为
display: flex; flex-direction: column - 每个手风琴项(
.accordion-item)设flex: 0 0 auto(禁止伸缩,初始 basis 由内容决定) - 展开时改为
flex: 1 0 fit-content或显式设flex-basis: fit-content(现代写法),再配合transition: flex-basis 0.3s ease - 旧版 Safari 需补
flex-basis: 0+min-height: 0防止撑开
如何用 JavaScript 控制 flex-basis 的切换逻辑
不能靠 CSS 类名 toggle 后让浏览器自动计算 fit-content —— 它不是可动画的关键词,必须转为具体像素值或 0。
推荐做法:展开前先强制触发重排(offsetHeight),再设 flex-basis 为目标高度;收起时设为 0。
立即学习“前端免费学习笔记(深入)”;
const item = document.querySelector('.accordion-item');
const content = item.querySelector('.accordion-content');
// 展开
item.style.flexBasis = '0';
item.offsetHeight; // 强制重排
item.style.flexBasis = `${content.scrollHeight}px`;
// 收起
item.style.flexBasis = '0';
- 避免用
getComputedStyle读取height,它可能返回auto,无法参与 transition - 如果内容动态加载,需在内容渲染完成后再调用上述逻辑(如
requestAnimationFrame或setTimeout(..., 0)) - 多个 item 同时操作时,注意不要批量重排引发性能抖动
flex-basis 过渡在 Safari 中卡顿或跳变
Safari 对 flex-basis: fit-content 的 transition 支持不稳定,尤其在嵌套 flex 容器中容易跳帧。
稳妥解法是退回到数值驱动:始终用 px 单位控制 flex-basis,并缓存内容高度。
- 首次展开后缓存
content.scrollHeight到data-height属性 - 后续展开直接读取该值,避免重复测量
- 监听窗口 resize 时清除缓存并重新测量(仅当内容高度可能变化时)
- 对
flex-basis加will-change: flex-basis(仅限频繁交互场景,慎用)
为什么不用 max-height 过渡替代 flex-basis
max-height 是常见“取巧”方案,但它有硬伤:必须预设一个远大于内容的值(如 max-height: 1000px),否则动画会提前截断;且空内容时仍有明显延迟感。
flex-basis 方案更精确,因为它直接绑定内容真实高度,无 magic number,也无需猜测最大值。
不过要注意:若手风琴项内含图片、iframe 等异步加载内容,scrollHeight 测量时机不对就会偏小——这时候得等资源加载完成,或用 ResizeObserver 监听内容盒变化。


















