For a thing I’m working on, I needed a ‘debounced‘ resize event, as the $(window).resize
event keeps firing as long as the user resizes his window. Maximizing the window with a single click just causes 1 event fired, but by dragging the browser window bigger/smaller, the event will fire lots and lots (and lots) of times. If your script depends on operations that need to be executed on a resize, and these operations are intense, this can cause your script to go haywire and hang/crash the browser. Besides that, it’s annoying to have your script do the same stuff over and over again.
In my search for a solution I stumbled across this great post by Paul Irish, where he basically handed me the solution; a debounced resize function. Worked great!
I decided to rewrite it into a function prototype, so regular JavaScript functions could be debounced easily, and to complement this I wrote an additional jQuery wrapper that can be used to debounce jQuery methods:
View/fork on Github: https://gist.github.com/c-kick/04a828c3d233db2504ec3a810217bfc3
Using this function prototype and jQuery wrapper, you can register new functions on the fly, based on their ‘regular’ counterparts. You will also need to define a timeout value, which specifies the time the function will wait to see if the event has stopped firing, after which it will finally execute the code you provide.
In the example below, I register both smartresize
, smartscroll
and smartmousemove
as the debounced counterparts of resize
, scroll
and mousemove
, and set them to a debounce delay of 100ms:
//register debouncing functions
//jQdeBounce(jQuery,'new eventname', 'original event', timeout);
//Note: keep the jQuery namespace in mind, don't overwrite existing functions!
jQdeBounce(jQuery,'smartresize', 'resize', 100);
jQdeBounce(jQuery,'smartscroll', 'scroll', 100);
jQdeBounce(jQuery,'smartmousemove', 'mousemove', 100);
//You can also debounce your own custom 'MyFunction' function,
//by using the deBounce function prototype when calling it:
MyFunction.deBounce(100, false); //calls 'MyFunction' debounced/throttled.
After this, usage is simple:
$(window).smartresize(function(e){
// do stuff as soon as the user stops resizing his browser for longer than 100ms
})
$(window).smartscroll(function(e){
// do stuff as soon as the user stops scrolling for longer than 100ms
})
$(window).smartmousemove(function(e){
// do stuff as soon as the user stops moving his mouse for longer than 100ms
})
Creating a ‘touchpause’ event
Another potential use is extending the touchmove
event: when the user touches (and moves his/her finger) across the screen, and then pauses, only the touchmove
event is fired. The touchend
event will only be fired when the user actually lets go of the screen. And since there is no ‘touchpause’ event, there is no easy way of telling if the user has paused sliding his finger across the screen. Using the deBounce wrapper, we can now create just that:
//define touchpause, extending touchmove
jQdeBounce(jQuery,'touchpause', 'touchmove', 100);
// bind it to something
$('#touched_element').touchpause(function(event) {
//touch event (and thus touchdata) is still available!
console.log('touchmove has paused!');
});
This way, when the user touches, drags (moves) and then pauses without leaving the screen, the touchpause
event is fired, allowing for some optional script execution. The event will also be fired when the user slides, does not pause, and then lets go, so keep that in mind.
Final note: Although this debouncing script is quite handy; if you need to use this for things other than scroll
, resize
, touchmove
and mousemove
events, your code logic might require some rethinking š it’s always better to prevent events from firing in sequence lots of times, rather than working around them.
[…] a JSON file and tweaked the CSS to display a bit better. I also found an improved version of a resize debouncer that helped eliminate some weird behavior on touch devices, but it also tied me to jQuery. Iād […]
[…] a JSON file and tweaked the CSS to display a bit better. I also found an improved version of a resize debouncer that helped eliminate some weird behavior on touch devices, but it also tied me to jQuery. Iād […]
Hi Klaas,
How do you unbind these? I’m not sure if that’s the right term here, but I want to be able to stop the event handling when condition x is true.
I can do this normally by doing:
$(window).on(‘scroll’, {el:target, sel:offsetFrom}, sticky);
$(window).off(‘scroll’, sticky);
But I am not sure how to apply the same idea with deBouncer.
Thanks
I would propose evaluating that condition inside the function that is triggered by the debouncer. Then there is no need to unbind. Let me know if that works for you!
Example:
Thank you for your response and code š
It works (jQuery 1.10), thank you!
Thanks for this. I was adding a handler to a HTML5 number spinner (type=”number”) and having a lot of trouble with the mouse scrollwheel flooding the browser with events! Your wrapper works perfectly for taming that stream.
Hi !
I am using your solution to resize and reposition images using something like this :
$(window).smartresize(function(e){
// here I change the position of my images and resize them
})
It works very well but I would like to start the function when the page load, I am quite new to jquery, can you help?
I need something like:
$(window).load(function() {
$(window).smartresize();// to force the execution of my repositioning and resizing…
});
Thanks in advance
raf
Hi raf,
just wrap your position changing in a function so you can call it both on load (though I think you need the .ready event of the document) and on smartresize.
Eg:
function handlePosition = function () {
//do stuff
}
and then:
$(window).smartresize(function() {
handlePosition();
}
$(document.ready(function() {
handlePosition();
}