如何区分JavaScript触发的滚动与用户手动触发的滚动?
区分JavaScript触发的滚动与用户手动滚动的方案
这个问题在做自动滚动类功能时确实很常见,原生的scroll事件本身并没有提供直接的属性来区分触发源,但我们可以通过几个简单的技巧来实现这个需求,下面给你详细讲两种可行的方案:
方案一:用全局标记变量区分
最直接的思路就是在触发自动滚动前设置一个标记,滚动完成后再重置,然后在scroll事件监听里判断这个标记:
// 全局标记:是否正在执行自动滚动 let isAutoScrolling = false; // 封装你的自动滚动函数 function triggerAutoScroll(targetY) { // 触发前标记为自动滚动 isAutoScrolling = true; // 执行滚动 window.scrollTo(0, targetY); // 用setTimeout确保scroll事件触发后再重置标记(因为scroll是同步触发的) setTimeout(() => { isAutoScrolling = false; }, 0); } // 监听滚动事件 window.onscroll = (event) => { if (!isAutoScrolling) { // 没有标记,说明是用户手动触发的滚动 activity = true; } };
如果你的自动滚动是平滑滚动(用behavior: 'smooth'),因为滚动是异步完成的,上面的setTimeout(0)可能会提前重置标记,这时候可以用scrollend事件来监听滚动结束:
function triggerAutoScroll(targetY) { isAutoScrolling = true; window.scrollTo({ top: targetY, behavior: 'smooth' }); // 滚动结束后重置标记,用once确保只触发一次 window.addEventListener('scrollend', function resetFlag() { isAutoScrolling = false; window.removeEventListener('scrollend', resetFlag); }, { once: true }); }
方案二:包装原生scrollTo方法(你提到的思路)
你说的复制scrollTo并添加标识的思路完全可行,而且更优雅——我们可以重写原生的scrollTo方法,给它增加一个自定义参数来标记是否是自动滚动:
// 先保存原生的scrollTo方法,避免覆盖后无法调用 const originalScrollTo = window.scrollTo; // 重写scrollTo方法 window.scrollTo = function(options) { let isAuto = false; // 如果传入的是配置对象,检查是否有我们自定义的isAuto属性 if (typeof options === 'object' && options.isAuto) { isAuto = true; // 删掉自定义属性,避免传给原生方法导致报错 delete options.isAuto; } // 如果是自动滚动,设置标记 if (isAuto) { isAutoScrolling = true; // 同样处理平滑滚动的情况 if (options.behavior === 'smooth') { window.addEventListener('scrollend', function resetFlag() { isAutoScrolling = false; window.removeEventListener('scrollend', resetFlag); }, { once: true }); } else { setTimeout(() => { isAutoScrolling = false; }, 0); } } // 调用原生的scrollTo方法 originalScrollTo.call(this, options); }; // 调用自动滚动时,加上isAuto标记 window.scrollTo({ top: 200, isAuto: true }); // 正常的用户手动滚动不会触发这个标记,所以在scroll事件里可以正常判断 window.onscroll = (event) => { if (!isAutoScrolling) { activity = true; } };
这个方案的好处是,所有通过这个重写的scrollTo调用的自动滚动都会被自动标记,不用每次都手动设置全局变量,代码更可维护。
注意事项
scrollend事件是比较新的API,如果你需要兼容旧浏览器(比如IE),可以用定时器来模拟滚动结束的判断,或者监听scroll事件,在一段时间内没有新的滚动触发时再重置标记。- 除了
scrollTo,如果你的自动滚动还用了scrollBy或者元素的scrollTop,也需要用同样的方式做标记处理。
内容的提问来源于stack exchange,提问作者Jansindl3r
相关产品推荐
相关产品推荐

