Nuxt中使用scrollTo平滑滚动跳转锚点时如何避免触发IntersectionObserver干扰
解决方案
核心问题是程序触发的平滑滚动过程中,路过的区块会触发IntersectionObserver的回调,篡改当前路由hash,打断滚动过程。我们可以通过加「滚动锁」的方式规避这个问题:
- 新增一个全局标记位,区分当前滚动是用户手动触发还是程序主动触发的平滑跳转
- 程序触发滚动时上锁,滚动期间IntersectionObserver回调直接跳过路由更新逻辑
- 滚动结束/用户手动中断滚动时解锁,恢复正常的路由同步逻辑
具体代码修改
1. 修改nuxt.config.js中的scrollBehavior配置
router: { scrollBehavior: async (to, from, savedPosition) => { if (savedPosition) { return savedPosition; } const findEl = async (hash, x) => { return ( document.querySelector(hash) || new Promise((resolve, reject) => { if (x > 50) { return resolve(); } setTimeout(() => { resolve(findEl(hash, ++x || 1)); }, 100); }) ); }; if (to.hash) { let el = await findEl(to.hash); if (!el) return { x: 0, y: 0 }; // 上锁:标记当前是程序主动触发的滚动 window.isProgrammaticScrolling = true; // 定义解锁逻辑,自动清理事件监听 const clearLock = () => { window.isProgrammaticScrolling = false; window.removeEventListener("scrollend", clearLock); window.removeEventListener("wheel", clearLock); window.removeEventListener("touchmove", clearLock); }; // 滚动结束自动解锁 window.addEventListener("scrollend", clearLock); // 用户手动滚动/触摸滑动时直接解锁,恢复observer功能 window.addEventListener("wheel", clearLock); window.addEventListener("touchmove", clearLock); if ("scrollBehavior" in document.documentElement.style) { return window.scrollTo({ top: el.offsetTop, behavior: "smooth" }); } else { // 不支持平滑滚动的浏览器滚动完成直接解锁 window.scrollTo(0, el.offsetTop); clearLock(); return; } } return { x: 0, y: 0 }; }, },
2. 修改IntersectionObserver的回调逻辑
sectionObserverHandler(entries) { // 程序滚动期间直接跳过路由更新 if (window.isProgrammaticScrolling) return; for (const entry of entries) { if (entry.isIntersecting) { const sectionId = entry.target.id; this.$router.replace({ name: this.$route.name, hash: `#${sectionId}`, }); } } },
兼容旧浏览器说明
如果需要兼容不支持scrollend事件的旧浏览器,可以把scrollend事件监听替换为定时器,按页面最长滚动时间设置延迟解锁即可,示例:
// 替换原来的scrollend监听逻辑 setTimeout(clearLock, 500);
内容的提问来源于stack exchange,提问作者Hans Felix Ramos
相关产品推荐
相关产品推荐

