You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义实现Bootstrap v5 Collapse时display:none致CSS过渡失效

问题原因

display: none状态的元素脱离正常渲染流,浏览器不会对同一事件循环内从display:none切换到可见、同时修改过渡属性的操作触发动画——所有样式变更会被合并到同一个渲染帧,浏览器直接渲染最终样式,跳过中间过渡过程。你当前的代码把移除collapse类(去掉display:none)、添加collapsing类、设置高度三个操作放在同一个同步执行流里,刚好踩中这个浏览器渲染机制的问题。

Bootstrap 官方collapse组件的实现逻辑

Bootstrap没有直接切换类名触发动画,而是通过分帧操作+强制重排+动态计算高度的方式绕开display对过渡的影响,完整流程分展开、收起两个方向:

展开流程

  • 第一步:移除元素上的collapse类(去掉display: none属性),添加collapsing类,此时元素高度为0、溢出隐藏,处于过渡初始状态
  • 第二步:读取元素的offsetHeight属性强制浏览器触发重排,让浏览器确认当前height:0的初始状态,避免样式合并
  • 第三步:将元素的内联height设置为元素真实内容高度(即element.scrollHeight,不是硬编码固定值),浏览器检测到高度差后启动过渡动画
  • 第四步:监听transitionend事件,等动画执行完成后,移除collapsing类,添加collapse和show类,同时清空内联height样式,让元素高度自适应内容

收起流程

  • 第一步:先把元素当前的实际高度通过内联样式固定,读取offsetHeight触发重排
  • 第二步:移除show类,添加collapsing类,将内联height设置为0,触发高度从当前值到0的过渡动画
  • 第三步:等动画执行完成后,移除collapsing类,加回collapse类,清空内联height样式,元素回到display: none的隐藏状态
修正后的可运行代码
.collapse:not(.show) {
  display: none;
}
.collapse {
  display: block;
  height: auto;
}
.collapsing {
  height: 0;
  overflow: hidden;
  transition: height 0.3s ease; /* 官方默认过渡时长为0.3s,可自行调整 */
}
#content {
  border: 1px solid black;
}
jQuery(function($) {
  const $content = $("#content");
  let isAnimating = false; // 动画锁,防止动画过程中重复点击导致状态错乱

  $(".toggle").on("click", function() {
    if (isAnimating) return;
    isAnimating = true;

    if ($content.hasClass("show")) {
      // 执行收起逻辑
      const currentHeight = $content.outerHeight();
      $content.css('height', currentHeight + 'px');
      
      // 强制浏览器重排,确认初始高度状态
      $content[0].offsetHeight;
      
      $content.removeClass("collapse show")
              .addClass("collapsing")
              .css('height', '0');

      // 动画结束后切换最终状态
      $content.one('transitionend', function() {
        $content.removeClass("collapsing")
                .addClass("collapse")
                .css('height', '');
        isAnimating = false;
      })
    } else {
      // 执行展开逻辑
      $content.removeClass("collapse")
              .addClass("collapsing");
      
      // 强制浏览器重排,确认初始高度状态
      $content[0].offsetHeight;
      
      // 计算内容真实高度,不硬编码固定值
      const targetHeight = $content[0].scrollHeight;
      $content.css('height', targetHeight + 'px');

      // 动画结束后切换最终状态
      $content.one('transitionend', function() {
        $content.removeClass("collapsing")
                .addClass("collapse show")
                .css('height', '');
        isAnimating = false;
      })
    }
  })
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="toggle">Toggle</button>
<div id="content" class="collapse">
  <p>Content Goes Here</p>
  <p>多行内容测试高度自适应,不管内容多少都能平滑过渡</p>
</div>
关键注意点
  • 强制重排是核心:必须在设置初始状态后、设置目标高度前读取布局属性(offsetHeight/clientHeight/getBoundingClientRect()均可),强迫浏览器完成一帧渲染,否则样式还是会被合并
  • 不要硬编码高度:用scrollHeight取内容真实高度,否则内容变化时动画会错位
  • 动画结束后一定要清空内联height样式,切换到height: auto状态,不然内部内容动态变化时会出现高度裁剪问题

内容的提问来源于stack exchange,提问作者Alaksandar Jesus Gene

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 17:18:24