固定侧边栏平滑滚动功能的Bug问题排查
解决锚点链接激活状态错误切换的问题
这个问题我之前也碰到过,核心原因是Intersection Observer在反向滚动时,可能同时有多个区块满足交叉条件,导致激活状态被错误切换。咱们一步步来分析和解决:
问题根源
你的代码里,只要有区块进入交叉区域(满足rootMargin: '0% 0% -80% 0%'),就会直接给对应链接加current类,但反向滚动时(比如从第4个区块回到第3个),第2个区块可能还没完全退出交叉区域,也会被Observer检测到,从而错误触发它的激活逻辑。另外还有个小bug:document.getElementById('#all-story')里多了个#,getElementById不需要带选择器前缀。
解决方案
我们需要调整Observer的回调逻辑,确保只有当前最靠近视口顶部的活跃区块对应的链接被激活,即使多个区块同时满足交叉条件也不会出错。
修改后的JavaScript代码
let mainNavLinks = document.querySelectorAll("a.link-timeline"); let mainSections = document.querySelectorAll("div.bloc-decennie"); // 修正root选择器,去掉多余的# const options = { root: document.getElementById('all-story'), rootMargin: '0% 0% -80% 0%', threshold: 0 } const observer = new IntersectionObserver((entries, observer) => { // 1. 过滤出所有正在交叉的区块 const intersectingEntries = entries.filter(entry => entry.isIntersecting); if (intersectingEntries.length === 0) return; // 2. 按区块的顶部位置排序,最靠近视口顶部的排在第一位 const activeEntry = intersectingEntries.sort((a, b) => { return a.boundingClientRect.top - b.boundingClientRect.top; })[0]; // 3. 找到对应的锚点链接 const link = document.querySelector(`[href="#${activeEntry.target.id}"]`); if (link) { // 移除所有链接的激活状态 mainNavLinks.forEach(item => item.classList.remove("current")); // 给当前区块对应的链接添加激活状态 link.classList.add("current"); } }, options); // 监听所有区块 mainSections.forEach(target => { observer.observe(target); });
关键修改点
- 修复root选择器错误:把
document.getElementById('#all-story')改成document.getElementById('all-story'),getElementById接收的是元素ID字符串,不需要带#前缀。 - 排序交叉条目:对所有满足交叉条件的区块按顶部位置排序,确保我们选中的是当前视口中最靠上的那个区块,避免反向滚动时的误触发。
- 容错处理:如果没有交叉的区块,直接返回,避免不必要的操作。
可选优化
如果想进一步减少多区块交叉的情况,可以调整rootMargin为0% 0% -90% 0%,这样只有区块的上10%进入视口才会触发Observer,降低多个区块同时满足条件的概率,但排序的方法是更通用的解决方案,适用于各种场景。
测试一下修改后的代码:从第4个链接跳转回第3个,应该只会激活第3个链接的current类,不会再错误触发第2个链接的状态了。
内容的提问来源于stack exchange,提问作者pinkeuse
相关产品推荐
相关产品推荐

