46

考虑以下网页:

 <html>
   <body onscroll="alert('body scroll event')">
     <div style='width:200px;height:200px;overflow:auto' onscroll="alert('div scroll event')">
       <div style='height:400px'>
       </div>
     </div>
   </body>
 </html>

此 html 创建一个带有滚动条的 div。如果移动滚动条,onscroll则会触发 div 元素上的事件。但是,onscroll不会触发 body 上的事件。这是意料之中的,因为 W3C 声明元素onscroll事件不会“冒泡”。

但是,我正在开发一个客户端 Web 框架,该框架需要知道任何时候滚动页面的任何元素上的滚动条。如果冒泡,这很容易做到onscroll,但不幸的是它没有。有没有其他方法可以检测onscroll整个页面的事件?(现在我主要关注 Webkit,所以一个特定于 Webkit 的解决方案会很好......)

以下是我尝试过的一些事情:

  1. 捕获DOMAttrModified(似乎不会因为移动滚动条而触发。)
  2. 使用 DOM 观察者(滚动条似乎也不会触发)
  3. 将事件类型更改onscroll为冒泡(似乎不可能)

似乎onscroll全局捕获事件的唯一方法是将onscroll事件附加到每个可能滚动的元素上,这非常难看并且会损害我的框架的性能。

有人知道更好的方法吗?

4

5 回答 5

99

在现代浏览器中检测所有滚动事件的最简单方法是在附加事件时使用“捕获”而不是“冒泡”:

window.addEventListener('scroll', function(){ code goes here }, true)

不幸的是,据我所知,在 <= IE8 等旧版浏览器中没有等价物

于 2015-10-05T17:37:28.203 回答
13

我有同样的问题。

最简单的方法当然是使用 jQuery。请注意,此方法可能会显着降低您的页面速度。此外,它不会考虑绑定事件后添加的任何新元素。

$("*").scroll(function(e) {
    // Handle scroll event
});

在 vanilla JavaScript 中,您可以在调用时将useCapture布尔值设置为,它将触发所有元素,包括动态添加的元素。trueaddEventListener

document.addEventListener('scroll', function(e) {
    // Handle scroll event
}, true);

请注意,这将在滚动事件实际发生之前触发。据我了解,事件有两个阶段。捕获阶段首先发生,从页面根(ownerDocument?)开始,向下遍历到事件发生的元素。在此之后是冒泡阶段,它从元素返回到根。

一些快速测试也表明这可能是 jQuery 处理它的方式(至少用于跟踪所有页面元素上的滚动),但我不是 100% 确定。

这是一个显示香草 JavaScript 方法的 JSFiddle http://jsfiddle.net/0qpq8pcf/

于 2015-03-03T14:56:36.060 回答
9

* ...蟋蟀鸣叫... *

好的,我想这个问题不会得到任何 stackoverflow 的喜爱,所以我不妨就我迄今为止找到的最佳解决方案回答我自己的问题,以防其他用户偶然发现这个问题:

我想出的最佳解决方案是为 BODY 元素捕获“onmousedown”和“onkeydown”:这些事件冒泡,因此如果用户尝试在页面上移动滚动条,这些全局函数将作为 by-产品。然后,在这些函数中,只需查找 event.target 并将临时“onscroll”事件附加到这些对象,直到鼠标/键再次“向上”。使用该方法,您可以避免“处理程序膨胀”并仍然全局捕获所有“onscroll”事件。(我认为这也适用于“鼠标滚轮”滚动,但我对最终皱纹的研究仍在进行中。)

于 2012-11-02T15:04:40.033 回答
1

当您想要在后台滚动任何内容后关闭对话框时,以下工作正常:

var scrollListener = function(e) {
    // TODO: hide dialog
    document.removeEventListener('scroll', scrollListener, true);
};

document.addEventListener('scroll', scrollListener, true);
于 2016-11-04T09:58:11.847 回答
0

我需要使用 shadowRoots 中的滚动事件在复杂的自定义元素中处理任何上下文中的滚动。这至少涵盖了我的场景中的部分问题,并且通常在这些较新的 Web 组件 API 的上下文中借鉴此处的答案和评论。到目前为止,在适当的生命周期回调中附加和分离侦听器效果很好(曾经可能不适合您的用例)。

      self.addEventListener('mousewheel', handler, {capture: true, once: true});
      self.addEventListener('keydown', handler, {capture: true, once: true});

还要注意,event.composedPath()如果需要有关滚动上下文的详细信息,它会提供通过所有 shadowRoots 和所有节点的整个事件路径。如果是这种情况,使用这种方法并将针对该特定场景的新处理程序附加到感兴趣的节点可能是有意义的。

于 2021-11-13T21:58:35.527 回答