抽屉高度动态过渡需用JS测量内容scrollHeight并设为CSS变量--drawer-target-height,再通过--drawer-height绑定height实现平滑过渡,避免使用height:auto或max-height。

抽屉高度怎么从 0 动态算到内容实际高度
直接用 height: 0 → height: auto 不会触发 CSS 过渡,浏览器无法插值计算 auto。必须把目标高度转成具体数值——而这个数值得靠 JS 读取并写入 CSS 变量。
关键思路:不操作元素内联样式,而是用 JS 测量内容高度后,设置一个自定义属性(如 --drawer-target-height),再让 CSS 的 height 依赖它过渡。
- 抽屉容器需有明确的
overflow: hidden,否则展开时内容会“撑开”父容器破坏动画 - 测量前确保内容已渲染、字体已加载、图片尺寸已知(可监听
load或用ResizeObserver) - 避免在
transition中使用max-height模拟——它容易因估算不准导致卡顿或截断
CSS 变量如何绑定到 transition 属性
CSS 变量本身不能直接用于 transition 的属性名(比如你不能写 transition: var(--height-prop) 0.3s ease),但可以把它作为 height 的值,并配合 transition: height 使用。
示例结构:
立即学习“前端免费学习笔记(深入)”;
.drawer {
height: var(--drawer-height, 0);
overflow: hidden;
transition: height 0.3s ease;
}
.drawer.open {
--drawer-height: var(--drawer-target-height);
}
注意:--drawer-height 必须在打开状态下被赋予有效值(如 240px),且初始状态要显式设为 0(不能只靠 fallback)。
- 变量名区分语义:用
--drawer-target-height存测量值,--drawer-height作为当前驱动值,避免混淆 - 不要在 :root 上全局定义这些变量,应限定在抽屉组件作用域内(比如加类名前缀或用 Shadow DOM)
- 如果同时控制多个抽屉,每个需独立变量(如
--drawer-1-target-height),否则会互相覆盖
JS 怎么安全地读高、设变量、避免布局抖动
核心是「测量 → 设置 → 触发重排」三步必须原子化,否则可能读到旧尺寸或触发多次 layout。
推荐做法:用 getBoundingClientRect() 读 scrollHeight(更可靠,含 padding/border),并在同一帧内完成设置:
const drawer = document.querySelector('.drawer');
const content = drawer.querySelector('.drawer-content');
// 确保内容已渲染(必要时 force reflow)
content.offsetHeight; // 触发 layout,确保尺寸就绪
const targetHeight = content.scrollHeight;
drawer.style.setProperty('--drawer-target-height', `${targetHeight}px`);
// 然后添加 open 类,触发 transition
drawer.classList.add('open');
- 不要用
offsetHeight读内容高度——它不包含overflow: hidden被裁掉的部分 - 避免在
requestAnimationFrame外反复读写(尤其循环中),易引发强制同步布局 - 折叠时,若内容动态变化过,记得重新测量再设变量,不能复用上次的
--drawer-target-height
为什么折叠收不严或展开一半就停
常见于变量未及时更新、CSS 优先级冲突、或 transition 被意外中断。最隐蔽的坑是:CSS 变量值带单位错误或为空字符串。
调试方法:
- 在 DevTools 的 Styles 面板里检查元素是否真有
--drawer-target-height: 240px,而不是--drawer-target-height: ""或NaNpx - 确认
.drawer.open类确实生效,且没有更高优先级的height: 100%或!important覆盖 - 检查是否误用了
display: none切换——它会让元素脱离文档流,scrollHeight返回 0 - 移动端 Safari 对 CSS 变量 + transition 的组合偶尔有渲染延迟,可加
transform: translateZ(0)强制 GPU 加速
真正麻烦的是异步内容(比如图片加载后高度突变),这时候光靠一次测量不够,得结合 ResizeObserver 监听内部尺寸变化并重设变量。


















