How to capture all scrolling events on a page without attaching an onscroll handler to every single container

Consider the following Web page:

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

This html creates a div with a scrollbar. If you move the scrollbar, the "onscroll" event on the div element is triggered. However, the "onscroll" event on the body is NOT fired. This is expected, since the W3C states that element onscroll events do not "bubble".

However, I'm developing a client-side web framework that needs to know any time a scroll bar on ANY element of the page is scrolled. This would be easy to do if "onscroll" bubbled, but unfortunately it does not. Is there any other way to detect onscroll events across an entire page? (Right now I'm focusing mainly on Webkit, so a Webkit-specific solution would be fine...)

Here are some things I've tried: 1. Capturing DOMAttrModified (doesn't seem to fire for moving scrollbars.) 2. Using DOM Observers (also don't seem to fire for scrollbars) 3. Changing the "onscroll" event type to bubble (seems to not be possible)

It seems the ONLY way to capture onscroll events globally is to attach an onscroll event to EVERY element that may scroll, which is very ugly and is going to hurt the performance of my framework.

Anyone know a better way?

Thanks in advance!


The simplest way to detect all scroll events in modern browser would be using 'capturing' rather than 'bubbling' when attaching the event:

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

Unfortunately as I am aware there is no equivalent in older browser such as <= IE8

*...crickets chirping... *

OK, I guess this question isn't going to get any stackoverflow love, so I might as well answer my own question as to the best solution I've found so far, in case another user stumbles across this question:

The best solution I've come up with is to capture "onmousedown" and "onkeydown" for the BODY element: These events bubble, and so if a user tries to move a scrollbar on the page these global functions will fire as a by-product. Then, in these functions, simply look up and attach a temporary "onscroll" event to those objects until the mouse/key is "up" again. Using that method, you can avoid "handler bloat" and still globally capture all "onscroll" events. (I think this will work for "mouse wheel" scrolling as well, but my research on that final wrinkle is still pending.)

I had this same issue.

The easiest way of course is to use jQuery. Be aware that this method could potentially slow down your page significantly. Also it will not account for any new elements that are added after the event is bound.

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

In vanilla JavaScript, you can set the useCapture boolean to true on your addEventListener call, and it will fire on all elements, including those added dynamically.

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

Note though that this will fire before the scroll event actually happens. As I understand it, there's two phases events go through. The capture phase happens first, and starts from the page root (ownerDocument?) and traverses down to the element where the event happened. After this comes the bubbling phase, which traverses from the element back up to the root.

Some quick testing too showed that this may be how jQuery handles it (for tracking scrolls on all page elements at least), but I'm not 100% sure.

Here's a JSFiddle showing the vanilla JavaScript method

The following works fine when you want to i.e. close a dialog after anything in the background is scrolled:

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

document.addEventListener('scroll', scrollListener, true);

Need Your Help

Custom Android Views in Eclipse Visual Editor

android eclipse android-layout adt visual-editor

In my applications, I often rely on custom build views, such as in the following example.