点击导航首项跳转至第三项时第二项出现active类高亮闪烁如何解决
你遇到的闪烁本质是两个原因叠加:
- 现有滚动监听逻辑是只要
scrollTop大于区块的offsetTop - 50就更新当前高亮,平滑滚动过程中必然会经过第二个区块的阈值,触发第二个按钮高亮,滚动到第三个区块后才切换,所以出现闪烁。 - scroll事件触发频率极高,没有做防抖处理,频繁更新DOM类名也会放大闪烁感知。
以下是两种可行解决方案:
方案1:用IntersectionObserver替换scroll事件监听(更推荐)
IntersectionObserver是浏览器原生提供的交叉观察者API,专门用来判断元素是否进入视口,可以精准控制触发高亮的条件,不会因为滚动路过就误触发,性能也更好。
修改后的JS代码:
const sectionOne = document.querySelectorAll('.observe'); const buttonList = document.querySelectorAll('button'); const box = document.querySelector('.box'); // 点击跳转时的锁,避免滚动过程中触发高亮 let isJumpIng = false; // 初始化交叉观察者 const observer = new IntersectionObserver((entries) => { // 跳转过程中直接返回,不处理 if(isJumpIng) return; entries.forEach(entry => { // 当区块进入视口超过50%时才高亮对应按钮 if(entry.isIntersecting && entry.intersectionRatio >= 0.5) { const currentId = entry.target.id; buttonList.forEach(btn => { btn.classList.remove('bg-red-500'); if(btn.classList.contains(currentId)) { btn.classList.add('bg-red-500'); } }) } }) }, { root: box, threshold: 0.5 // 可视比例达到50%触发,可根据需求调整 }) // 给所有区块注册观察 sectionOne.forEach(item => observer.observe(item)); // 跳转函数修改,加锁 function goTo(element) { isJumpIng = true; element.scrollIntoView({ behavior: "smooth" }); // 等滚动结束后解锁,根据滚动动画时长调整时间,一般平滑滚动是300-800ms setTimeout(() => { isJumpIng = false; // 解锁后手动触发一次当前可视区域的高亮修正 const current = [...sectionOne].find(item => { const rect = item.getBoundingClientRect(); const boxRect = box.getBoundingClientRect(); return rect.top >= boxRect.top && rect.top <= boxRect.top + 150; }) if(current) { buttonList.forEach(btn => { btn.classList.remove('bg-red-500'); if(btn.classList.contains(current.id)) { btn.classList.add('bg-red-500'); } }) } }, 600) } // 记得删掉HTML里box的onscroll绑定,以及原来的myScroll函数
方案2:给原有scroll逻辑加防抖+跳转锁(改动最小)
如果不想改原有逻辑,只要加防抖和跳转锁就能解决闪烁问题:
const sectionOne = document.querySelectorAll('.observe'); const buttonList = document.querySelectorAll('button') const box = document.querySelector('.box'); let isJumpIng = false; // 防抖函数,300ms内多次触发只执行最后一次,时长可根据需求调整 function debounce(fn, delay = 300) { let timer = null; return function(...args) { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay) } } const myScroll = debounce(function(e) { if(isJumpIng) return; let current = "" sectionOne.forEach((item) => { if (box.scrollTop >= (item.offsetTop - 50)) { current = item.getAttribute("id") } }) buttonList.forEach((button) => { button.classList.remove("bg-red-500") if (button.classList.contains(current)) { button.classList.add("bg-red-500") } }) }) // 跳转函数加锁 function goTo(element) { isJumpIng = true; element.scrollIntoView({ behavior: "smooth" }); setTimeout(() => { isJumpIng = false; // 手动触发一次滚动事件修正高亮 myScroll() }, 600) }
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

