Consider the following Web page:
<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>
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:
DOMAttrModified
(doesn't seem to fire for moving scrollbars.)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?
The scroll event does not bubble up. Although the event does not bubble, browsers fire a scroll event on both document and window when the user scrolls the entire page.
forEach(element => { window. addEventListener( "scroll", () => runOnScroll(element), { passive: true } ); }); Or alternatively, bind a single scroll listener, with evt => runOnScroll(evt) as handler and then figure out what to do with everything in elements inside the runOnScroll function instead.
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
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 http://jsfiddle.net/0qpq8pcf/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With