jQuery keyup Delay

12,618

Solution 1

You should look into the setTimeout and clearTimeout functions:

$(function() {
    var timer;
    $("#id").on('keyup',function() {
        timer && clearTimeout(timer);
        timer = setTimeout(postData, 300);
    });
});

Basically what we're doing is that instead of firing the postData function immediately, we're passing it to setTimeout, so that it fires after a given amount of time (in this case, 300 milliseconds).

The setTimeout function returns a timer ID, which we can then pass to clearTimeout to cancel that call from ever running. On every keypress, before we set a new timer, we first check if there's a timer set. If there is, we cancel the old timer before starting a new one.


To further enhance this, you can also abort your AJAX call on every keypress, just in case the user typed something after 300+ milliseconds (after the timer fires, but before the AJAX request returned).


P.S. You should check out Ben Alman's throttle/debounce plugin, which would work perfectly here.

Solution 2

Take a look at Underscore's debounce function - it makes this sort of behavior super straightforward:

$(function() {
  $('#id').on('keyup input', _.debounce(postData, 500))
}

This way, the call to postData will only occur after the user has stopped typing for 500ms (or whatever you set the debounce wait to)

Also, I snuck in the input event to catch additional changes like copy/paste in addition to keystrokes (in modern browsers)

Solution 3

I would delay the search a bit on keyup, but you also need to abort the last request since you can't predict what order they will execute (or you can use a queue for the ajax).

I'll describe all three parts:

Delay

Wrap the postData callback in setTimeout. Store the return value of setTimeout as $("#id").data('timeout') and call clearTimeout on it each time so that the event fires only once per fast string of characters typed.

Abort

Store the last request on $("#id").data('lastRequest') or the like. Each time postData is called, run $("#id").data('lastRequest').abort(). Store the return value of $.post() in that data field. To prevent an error, initialize like this:

$("#id").data('lastRequest', {abort: function () {}));

Queue

$("#id").data('lastRequest', true);
...
$.when($("#id").data('lastRequest')).then(function () {
   $("#id").data('lastRequest', $.post(...));
});
Share:
12,618
Tarquin
Author by

Tarquin

Updated on June 13, 2022

Comments

  • Tarquin
    Tarquin almost 2 years

    Hopefully this is a quick and easy one for someone to figure out. I am fairly new to using a lot of javascript/jquery, and I have the following setup to pull a customer name out of a database and display it as the user has finished typing the customer ID.

    All works great, but it searches at each keyup. I know I could change it to blur but I want it to search as they go, at a delay.

    Here is the current code:

    function postData(){
        var id = $('#id').val();
    
        $.post('inc/repairs/events-backend.php',{id:id},   
            function(data){
            $("#display_customer").html(data);
        });
        return false;
    }
    
    $(function() {
        $("#id").bind('keyup',function() {postData()});
    });
    

    As you can see it is binding to each keyup which means that if you search too quick, it may leave you with the wrong result as it was still being loaded. (ie if no matches are found, it displays an error, and typing fast enough leaves the user needing to backspace and retype the last number)

    Could someone assist in adding a delay to the existing code, and if possible a small explaination as to the change you made, I rather not just copy and paste without understanding.

    ---- EDIT ----

    This is the code I finished up with, thank you guys!

    function postData(){
        var id = $('#id').val();
    
        $.post('inc/repairs/events-backend.php',{id:id},   
            function(data){
            $("#display_customer").html(data);
        });
        return false;
    }
    
    $(function() {
        var timer;
        $("#id").bind('keyup input',function() {
            timer && clearTimeout(timer);
            timer = setTimeout(postData, 300);
        });
    });
    
  • Tarquin
    Tarquin over 11 years
    Thank you VERY Much for the quick reply, I understand it and will do a small amount of research on it. Implimenting it, was immediatly successful. I had looked at other ideas for the delay using the same technique but creating 2 additional functions, so again thank you greatly!
  • Nevir
    Nevir over 11 years
    This is going to end up calling postData for every keystroke - that's a lot of POSTing!
  • Tarquin
    Tarquin over 11 years
    The POST is done immediatly after the delay completes. The database returns the result and all is done. What benefit would I see with the queue? I strive to better my code so any tips are welcome of course!
  • pierdevara
    pierdevara over 11 years
    I don't believe so the conditional statement will be false while the user is typing, postData will be called only 3 seconds after the last keyup
  • Explosion Pills
    Explosion Pills over 11 years
    @Tarquin the reason to queue the actions is because you can't guarantee that the POSTs will complete in any particular order. With the queue, you enforce order but keep the POSTs asynchronous to the client.
  • Tarquin
    Tarquin over 11 years
    I like that this code works with input, generally we use CTRL + V, meaning it detects it and is issuing the POST, however on the off chance that someone right clicks and uses paste, this could too be useful. I must say I did use the code that joseph advised as it works very clean, so if there were an easy way to impliment Input into that, I may do it. Thanks for the tip!!!
  • Nevir
    Nevir over 11 years
    It's going to be called 3 seconds after the first keyup; and for every keyup following - if the user is still typing after the initial 3 secs; that's multiple posts, I think?
  • Tarquin
    Tarquin over 11 years
    Oh, so if i post and for some reason the server isnt fast at responding, then change what I type and it also posts, using the current code could result in either of the 2 results due to that?
  • Nevir
    Nevir over 11 years
    Joseph's approach should totally work with the input event (it's pretty much the same thing as the implementation of _.debounce) - just add input to the list of events and away you go!
  • Explosion Pills
    Explosion Pills over 11 years
    @Tarquin exactly, although that won't necessarily happy if you abort consistently. I think my queue code is also wrong because it could still screw up due to requests completing in the wrong order, but you could either use an array and $.when.apply instead or use something like this: gist.github.com/3818056
  • Joseph Silber
    Joseph Silber over 11 years
    Any reason you're not passing the postData function directly to debounce?
  • Tarquin
    Tarquin over 11 years
    Yeah looked it up, wasnt sure of syntax required but was amazingly easy. Again thank you for the tip on input, worth adding! :)
  • Tarquin
    Tarquin over 11 years
    I will look into it and figure out if its necessary for this particular implimentation, however for larger projects I think this will be extremly useful. I see it becoming Important for large amounts of traffic.
  • pierdevara
    pierdevara over 11 years
    The local variable will stay the same while the global will increment by the number of key presses since the first keyup, so the if statement will be false
  • Nevir
    Nevir over 11 years
    Ha, I have no good reason for not passing it directly >< Fixing!