Problem: Jumpy execution during UI changes

JavaScript has a few events (resize, scroll, touchmove, mousemove, etc), which keep firing during the whole duration of the action, not just once. That may serve some purpose sometimes but most of the time we want to do something AFTER the action is finished. This is called debouncing.

The classic case is windows resizing. When we have a responsive webpage, we want to resize our site’s interface to match the actual viewport size. While most of the work is done via CSS, we still might need to run some JS to complete the action. Our first attempt to handling this would be so something like this (in jQuery):

$(window).resize(function(){
// Do our things here
});

If you resize the window by dragging it, the event handler gets fired several times, causing the browser to redraw every time it’s called. And, if the event handler has some heavy operations in it, the page will get really sluggish. In some extreme cases, it can even crash the browser.

To prevent this, we need a means to execute the handler function once resizing has actually stopped. JavaScript does not provide an event to notify when resizing has stopped but we can get around this limitation using some nifty tricks.

Debouncing events firing

The goal behind debouncing functions is to reduce overhead by preventing a function from being called several times in rapid succession. Regardless of the library being used, all debounce functions are built on JavaScript’s native setTimeout function. While typically small in size, debounce functions take advantage of some rather advanced JavaScript concepts like closures and execution queues.

Use cases for a debouncing function

Use it to discard a number of fast-pace events until the flow slows down. Examples:

  • UI changes produced by continuous action, such as scrolling, window dragging or moving the mouse over a DIV.
  • When typing fast in a textarea that will be processed: you don’t want to start to process the text until user stops typing.
  • When saving data to the server via AJAX: You don’t want to spam your server with dozens of calls per second.



Example solution 1: use Underscore’s debounce function

Underscore.js is a rather small JavaScript library which provides utility functions for common programming tasks. I has a functional programming design instead of extending object prototypes like jQuery and Prototype do.

This library has a “debounce” function to get rid of the resizing event being fired too often by wrapping the handler in Underscore’s debouncing function. _.debounce(function, wait, [immediate])

What is does (from the official documentation) is creating and returning a new “debounced” version of the passed handler. This will postpone its execution until after “wait” milliseconds have elapsed since the last time it was invoked. So, now we can rewrite our code as:

$(window).resize(_.debounce(function(){
// Do your stuff here
console.log('Resized finished.');
}, 500));

A waiting time of 500 milliseconds works very well in most situations but you can play with times to get the result we want.

Example solution 2: Pure JavaScript debounce function

If we don’t want to use the Underscore library, we can use a native JavaScript function such as this one:

This function does the same as the “debounce” function by setting a timeout each time the “resize” event is fired.

If the event was not fired for at least 250ms, it is assumed that window resize has completed and then we proceed to do whatever it was supposed to happen if the windows changed its size. It is pretty useful if you don’t intend to use Underscore or another external library.

Final thoughts

As mentioned above, window resizing is the most common use case for debouncing, but you could also use it for key events that trigger an autocompleter, or any continuous or fast-pacing action.

Summary
Debouncing javascript events to prevent jerky action
Article Name
Debouncing javascript events to prevent jerky action
Description
The goal behind debouncing Javascript functions is to reduce overhead by preventing a function from being called several times in rapid succession.
Author
Publisher Name
Iván Melgrati
Publisher Logo