Peter Olson
Peter Olson

Reputation: 142921

Throttle event calls in jQuery

I have a keyup event bound to a function that takes about a quarter of a second to complete.

$("#search").keyup(function() {
  //code that takes a little bit to complete
});

When a user types an entire word, or otherwise presses keys rapidly, the function will be called several times in succession and it will take a while for them all to complete.

Is there a way to throttle the event calls so that if there are several in rapid succession, it only triggers the one that was most recently called?

Upvotes: 46

Views: 39807

Answers (6)

Max
Max

Reputation: 411

Two small generic implementations of throttling approaches. (I prefer to do it through these simple functions rather than adding another jquery plugin)

  1. Waits some time after last call

    This one is useful when we don't want to call for example search function when user keeps typing the query

function throttle(time, func) {
  if (!time || typeof time !== "number" || time < 0) {
      return func;
  }

  var throttleTimer = 0;

  return function() {
    var args = arguments;
    clearTimeout(throttleTimer);
    throttleTimer = setTimeout(function() {
      func.apply(null, args);
    }, time);
  }
}
  1. Calls given function not more often than given amount of time

    The following one is useful for flushing logs

function throttleInterval(time, func) {
  if (!time || typeof time !== "number" || time < 0) {
      return func;
  }

  var throttleTimer = null;
  var lastState = null;
  var eventCounter = 0;
  var args = [];

  return function() {
    args = arguments;
    eventCounter++;
    if (!throttleTimer) {
      throttleTimer = setInterval(function() {
        if (eventCounter == lastState) {
          clearInterval(throttleTimer);
          throttleTimer = null;
          return;
        }

        lastState = eventCounter;
        func.apply(null, args);
      }, time);
    }
  }
}

Usage is very simple:

The following one is waiting 2s after the last keystroke in the inputBox and then calls function which should be throttled.

$("#inputBox").on("input", throttle(2000, function(evt) {
  myFunctionToThrottle(evt);
}));

Here is an example where you can test both: click (CodePen)

Upvotes: 1

Michael Scheper
Michael Scheper

Reputation: 7048

You could also use the excellent Underscore/_ library.

Comments in Josh's answer, currently the most popular, debate whether you should really throttle the calls, or if a debouncer is what you want. The difference is a bit subtle, but Underscore has both: _.debounce(function, wait, [immediate]) and _.throttle(function, wait, [options]).

If you're not already using Underscore, check it out. It can make your JavaScript much cleaner, and is lightweight enough to give most library haters pause.

Upvotes: 6

AntonK
AntonK

Reputation: 2310

Here's a clean way of doing it with JQuery.

    /* delayed onchange while typing jquery for text boxes widget
    usage:
        $("#SearchCriteria").delayedChange(function () {
            DoMyAjaxSearch();
        });

    */
    (function ($) {
        $.fn.delayedChange = function (options) {
            var timer;
            var o;

            if (jQuery.isFunction(options)) {
                o = { onChange: options };
            }
            else
                o = options;

            o = $.extend({}, $.fn.delayedChange.defaultOptions, o);

            return this.each(function () {
                var element = $(this);
                element.keyup(function () {
                    clearTimeout(timer);
                    timer = setTimeout(function () {
                        var newVal = element.val();
                        newVal = $.trim(newVal);
                        if (element.delayedChange.oldVal != newVal) {
                            element.delayedChange.oldVal = newVal;
                            o.onChange.call(this);
                        }

                    }, o.delay);
                });
            });


        };

        $.fn.delayedChange.defaultOptions = {
            delay: 1000,
            onChange: function () { }
        }

        $.fn.delayedChange.oldVal = "";


    })(jQuery);

Upvotes: 2

josh3736
josh3736

Reputation: 144912

Take a look at jQuery Debounce.

$('#search').keyup($.debounce(function() {
    // Will only execute 300ms after the last keypress.
}, 300));

Upvotes: 70

Brian Armstrong
Brian Armstrong

Reputation: 19863

Something like this seems simplest (no external libraries) for a quick solution (note coffeescript):

running = false
$(document).on 'keyup', '.some-class', (e) ->
  return if running
  running = true
  $.ajax
    type: 'POST',
    url: $(this).data('url'),
    data: $(this).parents('form').serialize(),
    dataType: 'script',
    success: (data) ->
      running = false

Upvotes: -4

Nathan Manousos
Nathan Manousos

Reputation: 13848

Here is a potential solution that doesn't need a plugin. Use a boolean to decide whether to do the keyup callback, or skip over it.

var doingKeyup = false;

$('input').keyup(function(){
    if(!doingKeyup){
        doingKeyup=true;
        // slow process happens here
        doingKeyup=false;
    }
});

Upvotes: 13

Related Questions