How to delay the .keyup() handler until the user stops typing?

How to delay the .keyup() handler until the user stops typing?

I’ve got a search field. Right now it searches for every keyup. So if someone types “Windows”, it will make a search with AJAX for every keyup: “W”, “Wi”, “Win”, “Wind”, “Windo”, “Window”, “Windows”.
I want to have a delay, so it only searches when the user stops typing for 200 ms.
There is no option for this in the keyup function, and I have tried setTimeout, but it didn’t work.
How can I do that?

Solutions/Answers:

Solution 1:

I use this small function for the same purpose, executing a function after the user has stopped typing for a specified amount of time or in events that fire at a high rate, like resize:

function delay(callback, ms) {
  var timer = 0;
  return function() {
    var context = this, args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function () {
      callback.apply(context, args);
    }, ms || 0);
  };
}


// Example usage:

$('#input').keyup(delay(function (e) {
  console.log('Time elapsed!', this.value);
}, 500));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="input">Try it:
<input id="input" type="text" placeholder="Type something here..."/>
</label>

How it works:

The delay function will return a wrapped function that internally handles an individual timer, in each execution the timer is restarted with the time delay provided, if multiple executions occur before this time passes, the timer will just reset and start again.

When the timer finally ends, the callback function is executed, passing the original context and arguments (in this example, the jQuery’s event object, and the DOM element as this).

UPDATE 2019-05-16

I have re-implemented the function using ES5 and ES6 features for modern environments:

function delay(fn, ms) {
  let timer = 0
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(fn.bind(this, ...args), ms || 0)
  }
}

The implementation is covered with a set of tests.

For something more sophisticated, give a look to the jQuery Typewatch plugin.

Solution 2:

If you want to search after the type is done use a global variable to hold the timeout returned from your setTimout call and cancel it with a clearTimeout if it hasn’t yet happend so that it won’t fire the timeout except on the last keyup event

var globalTimeout = null;  
$('#id').keyup(function(){
  if(globalTimeout != null) clearTimeout(globalTimeout);  
  globalTimeout =setTimeout(SearchFunc,200);  
}   
function SearchFunc(){  
  globalTimeout = null;  
  //ajax code
}

Or with an anonymous function :

var globalTimeout = null;  
$('#id').keyup(function() {
  if (globalTimeout != null) {
    clearTimeout(globalTimeout);
  }
  globalTimeout = setTimeout(function() {
    globalTimeout = null;  

    //ajax code

  }, 200);  
}   

Solution 3:

Another slight enhancement on CMS’s answer. To easily allow for separate delays, you can use the following:

function makeDelay(ms) {
    var timer = 0;
    return function(callback){
        clearTimeout (timer);
        timer = setTimeout(callback, ms);
    };
};

If you want to reuse the same delay, just do

var delay = makeDelay(250);
$(selector1).on('keyup', function() {delay(someCallback);});
$(selector2).on('keyup', function() {delay(someCallback);});

If you want separate delays, you can do

$(selector1).on('keyup', function() {makeDelay(250)(someCallback);});
$(selector2).on('keyup', function() {makeDelay(250)(someCallback);});

Solution 4:

You could also look at underscore.js, which provides utility methods like debounce:

var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);

Solution 5:

Based on the answer of CMS, I made this :

Put the code below after include jQuery :

/*
 * delayKeyup
 * http://code.azerti.net/javascript/jquery/delaykeyup.htm
 * Inspired by CMS in this post : http://stackoverflow.com/questions/1909441/jquery-keyup-delay
 * Written by Gaten
 * Exemple : $("#input").delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);
 */
(function ($) {
    $.fn.delayKeyup = function(callback, ms){
        var timer = 0;
        $(this).keyup(function(){                   
            clearTimeout (timer);
            timer = setTimeout(callback, ms);
        });
        return $(this);
    };
})(jQuery);

And simply use like this :

$('#input').delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);

Careful : the $(this) variable in the function passed as a parameter does not match input

Solution 6:

Delay Multi Function Calls using Labels

This is the solution i work with. It will delay the execution on ANY function you want. It can be the keydown search query, maybe the quick click on previous or next buttons ( that would otherwise send multiple request if quickly clicked continuously , and be not used after all). This uses a global object that stores each execution time, and compares it with the most current request.

So the result is that only that last click / action will actually be called, because those requests are stored in a queue, that after the X milliseconds is called if no other request with the same label exists in the queue!

function delay_method(label,callback,time){
    if(typeof window.delayed_methods=="undefined"){window.delayed_methods={};}  
    delayed_methods[label]=Date.now();
    var t=delayed_methods[label];
    setTimeout(function(){ if(delayed_methods[label]!=t){return;}else{  delayed_methods[label]=""; callback();}}, time||500);
  }

You can set your own delay time ( its optional, defaults to 500ms). And send your function arguments in a “closure fashion”.

For example if you want to call the bellow function:

function send_ajax(id){console.log(id);}

To prevent multiple send_ajax requests, you delay them using:

delay_method( "check date", function(){ send_ajax(2); } ,600);

Every request that uses the label “check date” will only be triggered if no other request is made in the 600 miliseconds timeframe. This argument is optional

Label independency (calling the same target function) but run both:

delay_method("check date parallel", function(){send_ajax(2);});
delay_method("check date", function(){send_ajax(2);});

Results in calling the same function but delay them independently because of their labels being different