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方法,让它重新计算所有目标元素的位置:
- 提前获取Scrollspy实例,方便后续调用刷新方法;
- 在
shiftContentUp和shiftContentDown执行完DOM修改后,调用scrollSpyInstance.refresh(); - 确保滚动操作完成后立即触发刷新,让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
相关产品推荐
相关产品推荐

