After recently considering the impact of a native DOM implementation of two possible scroll events; “scrollstop” and “scrollstart”, I eventually came to the realisation that it would probably be a bad idea. Consider these two potential events yourself – what would initiate the “scrollstop” event? – how does the browser know when a user has stopped scrolling; it can’t just fire the event on every single step of a scroll (like the native “scroll” event)…
No, the only way to even simulate such an event would be to have a delay between the last scrolling action and the event itself – a timer could start on every “scroll” event and then if that timer is not cleared in time (say 500 milliseconds) then it can be assumed a user has “stopped scrolling”, or, has taken a break from a lengthy scroll; either circumstance warrants a “scrollstop” event in my opinion.
While we’re at it we may as well add a “scrollstart” event – this will only fire once at the start of each scrolling session.
I had always wanted a decent psuedo-event to create using jQuery’s cool “Special Events” API – when I first heard about it I wasn’t sure how I might make use of it. By the way, jQuery uses this technique to implement the non-cross-browser “mousenter” and “mouseleave” events as well as the infamous “ready” event! Here, we can use it to create our two new events, “scrollstart” and “scrollstop”.
Because of jQuery’s (relatively) simple API, creating these new events couldn’t be much easier:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | (function(){ var special = jQuery.event.special, uid1 = 'D' + (+new Date()), uid2 = 'D' + (+new Date() + 1); special.scrollstart = { setup: function() { var timer, handler = function(evt) { var _self = this, _args = arguments; if (timer) { clearTimeout(timer); } else { evt.type = 'scrollstart'; jQuery.event.handle.apply(_self, _args); } timer = setTimeout( function(){ timer = null; }, special.scrollstop.latency); }; jQuery(this).bind('scroll', handler).data(uid1, handler); }, teardown: function(){ jQuery(this).unbind( 'scroll', jQuery(this).data(uid1) ); } }; special.scrollstop = { latency: 300, setup: function() { var timer, handler = function(evt) { var _self = this, _args = arguments; if (timer) { clearTimeout(timer); } timer = setTimeout( function(){ timer = null; evt.type = 'scrollstop'; jQuery.event.handle.apply(_self, _args); }, special.scrollstop.latency); }; jQuery(this).bind('scroll', handler).data(uid2, handler); }, teardown: function() { jQuery(this).unbind( 'scroll', jQuery(this).data(uid2) ); } }; })(); |
Now we can use our two new events as if they were native DOM events (within jQuery). And, because we’ve used the “Special Events” API our events are immediately operable via all event methods, like bind
, one
, and unbind
.
I’ve set up a demo which shows the two new scroll events in action: HERE!
To learn more about jQuery’s “Special Events” API you should definitely read through this article by Brandon Aaron. The API’s usefulness, I hope, has become apparent through this post of mine.
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
Great plugin! Just need to figure out how and when to use it.
Wow, I’ve struggling with scrollends for a horizontal scroll just this week for a new project. I found a way to solve several issues but with your special event it makes my code a whole lot easier. Good job you did there. Tested it in Safari 4.0 and Firefox 3.5 on a mac and works just fine! Keep up the good work!
Excellent writeup, thanx! As you said, now with your example, mr Aarons Special Events article suddenly makes perfect sense.
Just to nitpick a bit (and make sure I have actually grasped the goings on) – shouldn’t you, in both teardown functions, unbind the scroll event? Since now, if I bind scrollstart/stop to an element, and then for whatever reason unbind the same event, the scroll event (bound by the scrollstart/stop setup) will still be bound, and I only THINK I’ve made my bed. 🙂
@David, Thanks! I completely forgot that part; jQuery takes care of all the handling but I forgot that I’d bound an additional handler that jQuery didn’t know was related… Anyway, all fixed now! Thanks! 🙂
Gr8 plugin!! will really help me! Thanks for sharing
Very clever solution, good work.
Nice plugin! Thinking of implementing in registration pages or some lazy load pages.
I’ve been in need of a “scrollstop” event for a project I’m working on and this worked flawlessly… the first time! You are a true jQuery gangsta! 🙂
Thanks,
Ben
Hi james… I want to put your code together to mine on a open source plugin handling scrolling events. There’s no License reference on your code.
Do you mind if I release this plugin with your code together (obviously with the reference to yours)?