如何在scroll-behavior: smooth场景下区分滚动来源并控制scroll事件
解决手动滚动触发scroll事件、锚点平滑滚动不触发的需求
需求场景:页面绑定了scroll事件,同时通过scroll-behavior: smooth实现锚点链接的平滑滚动,需要让scroll事件仅在用户手动滚动时执行,点击锚点触发的平滑滚动过程中不执行该事件。
解决方案思路
通过标记滚动来源的状态变量,结合scrollend事件监听滚动结束,来控制scroll事件的执行逻辑:
- 点击锚点链接时,标记当前是「程序触发的滚动」
- scroll事件触发时,先判断状态,若为程序滚动则跳过执行
- 监听
scrollend事件,在平滑滚动结束后重置状态,恢复手动滚动的事件处理
修改后的完整代码
HTML
<!DOCTYPE html> <html> <head> <title>Parcel Sandbox</title> <meta charset="UTF-8" /> <link rel="stylesheet" href="./src/styles.css" /> </head> <body> <nav> <ul> <li><a href="#section1">Section 1</a></li> <li><a href="#section2">Section 2</a></li> <li><a href="#section3">Section 3</a></li> </ul> </nav> <section id="section1">Section 1</section> <section id="section2">Section 2</section> <section id="section3">Section 3</section> </body> </html>
CSS
html { scroll-behavior: smooth; } body { font-family: Arial; } section { display: flex; justify-content: center; align-items: center; height: 150vh; font-size: 40px; color: #fff; } section:nth-of-type(1) { background-color: black; } section:nth-of-type(2) { background-color: grey; } section:nth-of-type(3) { background-color: black; }
JavaScript
let isProgrammaticScroll = false; function scrollHandler() { // 如果是程序触发的滚动,直接返回不执行逻辑 if (isProgrammaticScroll) return; console.log("scroll"); } const throttledScrollHandler = throttle(scrollHandler, 200); window.addEventListener("scroll", throttledScrollHandler); window.addEventListener("click", (e) => { const anchor = e.target.closest("a"); if (anchor && anchor.getAttribute('href').startsWith('#')) { isProgrammaticScroll = true; console.log("clicked"); } }); // 监听滚动结束事件,重置状态 window.addEventListener("scrollend", () => { isProgrammaticScroll = false; }); function throttle(callback, delay) { let timer = null; return function throttledFunc(...args) { if (timer) { return; } timer = setTimeout(() => { callback(...args); clearTimeout(timer); timer = null; }, delay); }; }
关键说明
isProgrammaticScroll变量用来区分滚动是用户手动触发还是锚点平滑滚动触发- 点击锚点时设置状态为
true,此时scroll事件触发会直接跳过执行 scrollend事件会在平滑滚动完全结束后触发,此时重置状态为false,确保后续手动滚动能正常触发scroll事件- 对锚点链接做了额外判断(
href以#开头),避免误触普通链接的点击事件
内容的提问来源于stack exchange,提问作者Natalia Davydova
相关产品推荐
相关产品推荐

