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

Bootstrap 5.2 Scrollspy向上滚动激活失效问题求助

Bootstrap 5.2 Scrollspy 顶部滚动加载失效问题

我在用Bootstrap 5.2的Scrollspy功能,滚动到scrollBox顶部的pre-reload-item或底部的post-reload-item区域时更新内容。底部的shiftContentDown函数运行正常,但shiftContentUp仅首次生效,之后Scrollspy无法再激活reload-top元素——除非先滚动到底部触发reload-bottom及shiftContentDown,再向上滚动才会恢复。

我怀疑问题出在document.getElementById('scrollBox').scrollBy(0, count * 24)这行代码:

  • 去掉该行,pre-reload-item的新内容会处于焦点,reload-top保持激活状态;
  • 保留该行,原pre-reload-item的内容移至mid-reload-item并保持可见,reload-top不再激活,且后续向上滚动也无法触发它。

问题根源

Scrollspy的激活逻辑依赖目标元素在视口中的位置判断。当你修改DOM结构(添加/移除内容元素)或调用scrollBy改变滚动位置后,Scrollspy不会自动重新计算元素的位置信息,导致它无法正确识别pre-reload-item的当前位置,从而无法触发reload-top的激活状态。

解决方案

核心是在修改DOM结构和执行滚动操作后,手动调用Scrollspy的refresh方法,让它重新计算所有目标元素的位置:

  1. 提前获取Scrollspy实例,方便后续调用刷新方法;
  2. 在shiftContentUp和shiftContentDown执行完DOM修改后,调用scrollSpyInstance.refresh();
  3. 确保滚动操作完成后立即触发刷新,让Scrollspy同步最新的布局状态。

修改后的代码

JavaScript

let nextIndex = 0;
let allContent = [];
let displayed = [0, 0]
let eof = false;
let pre = document.getElementById('pre-reload-item');
let mid = document.getElementById('mid-reload-item');
let post = document.getElementById('post-reload-item');
// 初始化并获取Scrollspy实例
const scrollSpyInstance = new bootstrap.ScrollSpy(document.getElementById('scrollBox'), {
  target: '#indexBox',
  offset: 0
});

document.getElementById('scrollBox').addEventListener('activate.bs.scrollspy', () => {
  let current = document.querySelector('#indexBox > a.active');
  if (current === document.getElementById('reload-bottom')) {
    shiftContentDown()
  } else if (current === document.getElementById('reload-top')) {
    shiftContentUp()
  }
});

function shiftContentDown(count = 100, buffer = 0.5) {
  let shift = allContent.length - displayed[1];
  if (!eof && shift < count) {
    getMoreContent(nextIndex);
  } else {
    let addition = allContent.slice(displayed[1], displayed[1] + count);
    addition.forEach(l => {
      if (pre.childElementCount >= (count * buffer)) {
        pre.removeChild(pre.childNodes[0]);
        displayed[0]++;
      }
      if (mid.childElementCount >= (count * (1 + 2*buffer)))
        pre.appendChild(mid.childNodes[0]);
      if (post.childElementCount >= (count * buffer) || post.childNodes.length > mid.childNodes.length)
        mid.appendChild(post.childNodes[0]);
      post.appendChild(l);
      displayed[1]++;
    });
    // DOM修改后刷新Scrollspy
    scrollSpyInstance.refresh();
  }
}

function shiftContentUp(count = 100) {
  let addition = allContent.slice(Math.max(displayed[0] - count, 0), displayed[0]);
  addition.reverse().forEach(l => {
    pre.insertBefore(l, pre.childNodes[0]);
    displayed[0]--;
    mid.insertBefore(pre.childNodes[pre.childElementCount - 1], mid.childNodes[0]);
    post.insertBefore(mid.childNodes[mid.childElementCount - 1], post.childNodes[0]);
    post.removeChild(post.childNodes[post.childElementCount - 1]);
    displayed[1]--;
  });
  document.getElementById('scrollBox').scrollBy(0, count * 24);
  // 滚动后刷新Scrollspy,同步最新位置
  scrollSpyInstance.refresh();
}

function getMoreContent(index, chunkSize=100, last=1000) {
  for (let i=0; i < chunkSize; i++) {
    if (nextIndex < last) {
      let newLineNode = document.createElement('div');
      newLineNode.innerHTML = '----- ' + (nextIndex++) + ' -----';
      allContent.push(newLineNode);
    } else {
      eof = true;
    }
  }
  shiftContentDown();
}

HTML

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">

<div id="indexBox" class="list-group" style="display: none">
  <a id="reload-bottom" class="list-group-item list-group-item-action" href="#post-reload-item">reload bottom</a>
  <a id="reload-top" class="list-group-item list-group-item-action" href="#pre-reload-item">reload top</a>
</div>
<div id="scrollBox" data-bs-spy="scroll" data-bs-target="#indexBox" data-bs-smooth-scroll="true" data-bs-offset="0" class="scrollspy-example" tabindex="0" style="height: 300px; overflow-y: scroll">
  <div id="pre-reload-item" class="text-bg-danger" style="white-space: pre-line"></div>
  <div id="mid-reload-item" class="text-bg-info" style="white-space: pre-line"></div>
  <div id="post-reload-item" class="text-bg-danger" style="white-space: pre-line"></div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 06:45:21