如何在JS中强制重绘,实现CSS height属性的过渡动画?
解决CSS height:auto到固定高度过渡的JS实现问题
我完全理解你遇到的这个CSS过渡痛点——截至2019年,height: auto到固定高度的平滑过渡确实无法仅通过CSS实现,必须借助JavaScript手动干预。你希望在同一个函数内完成高度设置、强制重绘(用户无感知)、触发过渡,同时避免那种依赖不确定超时时间的setTimeout hack,这个需求非常合理。
问题根源分析
浏览器的DOM更新和重绘是批量处理的:如果你连续执行box.style.height = 计算值再box.style.height = "0",浏览器会合并这两个操作,认为没有中间状态需要渲染,自然不会触发过渡动画。之前尝试的console.log(box.offsetHeight)无效,大概率是因为浏览器的优化机制,读取布局属性的操作并没有强制触发即时重绘。
可靠的解决方案:使用requestAnimationFrame
requestAnimationFrame会将回调函数加入浏览器的渲染队列,确保在下一次重绘完成后执行回调。这就保证了第一次设置高度的操作已经被浏览器渲染,再执行设为0的操作时,浏览器能捕捉到高度变化的两个状态,从而正常触发过渡。
修改后的代码如下:
function toggleBox(e) { var box = document.querySelector(".box table"); if(e.innerHTML == "-") { if(!(box.getAttribute("data-height") > 0)) { const computedHeight = box.getBoundingClientRect().height; box.setAttribute("data-height", computedHeight); box.style.height = `${computedHeight}px`; // 借助requestAnimationFrame确保重绘后再触发过渡 requestAnimationFrame(() => { box.style.height = "0"; }); } else { // 已存储过高度,直接触发收缩过渡 box.style.height = "0"; } e.innerHTML = "+"; } else { box.style.height = `${box.getAttribute("data-height")}px`; e.innerHTML = "-"; } }
代码说明
- 首先获取元素的实际高度并存储到
data-height属性中,同时将元素的height设为该值,让浏览器渲染这个状态。 - 通过
requestAnimationFrame延迟设置height: 0,确保浏览器已经完成上一次高度设置的重绘,这样过渡动画就能正常触发。 - 再次点击展开时,直接读取存储的高度值设置回去即可。
补充注意事项
- 你的CSS中已经设置了
overflow: hidden和transition: height 0.2s ease-out,这是过渡生效的必要条件,无需修改。 - 相比
setTimeout,requestAnimationFrame完全依赖浏览器的渲染周期,不会出现超时过短无效、过长导致延迟的问题,是更优雅的非hack方案。
内容的提问来源于stack exchange,提问作者MiK
相关产品推荐
相关产品推荐

