Using Underscore.js’s debounce() to filter double-clicks

December 12, 2012

Watching users double-click a form button always makes me cringe. Worse: knowing that AJAX is firing with each click. Instead if flooding our server with AJAX requests it would be nice to limit how frequently the click handler runs. Fortunately, libraries like Underscore.js have wonderful functions like throttle and debounce do just that.

But which do you use?

A quick JavaScript example

The documentation for throttle indicates the passed function will be called at most “once per every wait milliseconds”, whereas debounce will execute the function on the trailing/leading edge of the wait period. Personally, I find this hard to understand without an example. Check out this jsFiddle that visually explains the difference:


The top set of “lights” uses throttle. Click on the top example once and watch the first light show up, then really hammer on that mouse button. You’ll see the green lights go on one-by-one, but there will always be at least a 1-second wait between each. throttle doesn’t stop the click handler from running, rather it’s just limiting it to run once a second. The gotcha: subsequent clicks get rolled into 1 click event that will fire. That leads to the “laggy” second light turning on.

The bottom set of “lights” uses debounce with a third parameter set to true so it fires its event on the first click. Try hammering your mouse button again and notice no matter how many times you click the additional lights won’t turn on. Why? Subsequent clicks are filtered out and they reset the wait time. The gotcha: the user still has to wait a whole second for the handler to return to firing events.

Other ways to defeat that secondary click … or not

There are some other ways to defeat a secondary click which can be a bit obtuse:

  • jQuery’s one(): If you really only want something to fire once and only once one is great. (Underscore.js also has once.) However, if you still plan on reusing the targeted element you’ll have to rebind your event handler.
  • Ignore the built-in dblclick event: You could prevent the element’s dblclick, but the original click event still fires. This doesn’t really work to throttle the clicks.

Other lessons we learned

Through testing we have found that debounce works really well with a wait time around 500ms.