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

窗口resize时如何正确重算嵌套手风琴下拉块的高度

问题场景

现有带嵌套层级的下拉手风琴菜单,为实现平滑展开动画效果,页面加载时会计算每个内容块的高度,将高度值写入块的dataset自定义属性后把块高度设为0实现隐藏;窗口触发resize事件时,尝试复用相同逻辑重新计算高度,对应实现代码如下:

function activeHeight() {
    let allAccordion = document.querySelectorAll('.accordion');

    allAccordion.forEach((item) => {
      let accordionParent = item.querySelectorAll('.accordion-parent');
      let accordionItem = item.querySelectorAll('.accordion-item');

      accordionItem.forEach((item) => {
        item.nextElementSibling.style.height = 'auto';
        item.nextElementSibling.dataset.height = item.nextElementSibling.offsetHeight;
        item.nextElementSibling.style.height = '0';
      });

      accordionItem.forEach((item) => {
        if (item.classList.contains('active')) {
          let element = item.closest('.accordion-parent');
          if (element?.querySelectorAll('.accordion-item.active')) {
            const allElement = element.querySelectorAll('.accordion-item.active');

            allElement.forEach((item) => {
              let innerElement = item.nextElementSibling.querySelectorAll('.accordion-item.active');

              let countInnerElement = +item.nextElementSibling.dataset.height;

              innerElement.forEach((element) => {
                countInnerElement += +element.nextElementSibling.dataset.height;
              });

              item.nextElementSibling.style.height = countInnerElement + 'px';
            });
          }
        }
      });
    });
  }

代码运行后出现高度计算结果不一致、数值异常的问题,无法得到正确的块高度,相关异常效果参考截图:
手风琴高度计算异常效果1
手风琴高度计算异常效果2
手风琴高度计算异常效果3

问题根因
  • 测量逻辑时序错误:第一轮遍历逐一把块设为auto、读高度、立刻设为0,这个过程中嵌套的子块还处于高度0的隐藏状态,读到的父块高度没有包含子项内容,存入dataset的基础高度值本身就是错误的。
  • 高度累加重复计算:遍历激活项累加子项高度时没有做层级限制,会把深层嵌套的子项高度重复加到每一层父块上,最终算出的高度远大于实际值。
  • 缺少强制重排步骤:修改DOM样式后没有触发浏览器重排,部分场景下读到的offsetHeight是浏览器缓存的旧值,导致计算结果随机不一致。
  • 存在冗余代码:声明了未使用的变量accordionParent,不影响功能但会增加代码维护成本。
修复方案

调整计算逻辑:先把所有内容块重置为自动高度,触发浏览器重排后,先记录每个块自身不含嵌套子项的原生高度,再按嵌套深度从深到浅处理激活项,只累加直接子激活项的高度,避免重复计算。修复后完整代码如下:

function activeHeight() {
  const allAccordion = document.querySelectorAll('.accordion');
  allAccordion.forEach(accordion => {
    // 选中所有手风琴内容块(即.accordion-item的相邻下一个节点)
    const allContentBlocks = accordion.querySelectorAll('.accordion-item + *');
    
    // 第一步:重置所有块状态准备测量
    allContentBlocks.forEach(block => {
      block.style.height = 'auto';
      block.style.overflow = 'hidden';
    });
    // 触发强制重排,确保auto高度真实渲染
    void accordion.offsetHeight;

    // 第二步:存储每个块自身的基础高度,重置为隐藏状态
    allContentBlocks.forEach(block => {
      block.dataset.selfHeight = block.offsetHeight;
      block.style.height = '0';
    });

    // 第三步:从最内层激活项开始向外逐层计算总高度
    const allActiveItems = Array.from(accordion.querySelectorAll('.accordion-item.active'));
    // 按嵌套深度排序,嵌套层级越深的项越先处理
    allActiveItems.sort((a, b) => b.querySelectorAll('.accordion-item').length - a.querySelectorAll('.accordion-item').length);
    
    allActiveItems.forEach(activeItem => {
      const contentBlock = activeItem.nextElementSibling;
      let totalHeight = Number(contentBlock.dataset.selfHeight);
      // 只累加直接子级的激活块高度,不跨层重复统计
      const directChildActiveBlocks = contentBlock.querySelectorAll(':scope > .accordion-parent > .accordion-item.active + *');
      directChildActiveBlocks.forEach(childBlock => {
        totalHeight += Number(childBlock.offsetHeight);
      });
      contentBlock.style.height = `${totalHeight}px`;
    });
  });
}
配套优化
  • 给所有手风琴内容块加基础样式,保证动画效果正常:
.accordion-item + * {
  overflow: hidden;
  transition: height 0.25s ease;
}
  • 给resize事件加防抖,避免窗口拖动时频繁触发计算导致卡顿:
let resizeTimer = null;
window.addEventListener('resize', () => {
  clearTimeout(resizeTimer);
  resizeTimer = setTimeout(activeHeight, 120);
});

内容的提问来源于stack exchange,提问作者narem

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 15:36:24