jquerytimeoutkeypresstypeaheaddebouncing

How to trigger an event in input text after I stop typing/writing?


I want to trigger an event just after I stop typing (not while typing) characters in my input textbox.

I've tried with:

$('input#username').keypress(function() {
    var _this = $(this); // copy of this object for further usage

    setTimeout(function() {
        $.post('/ajax/fetch', {
            type: 'username',
            value: _this.val()
        }, function(data) {
            if(!data.success) {
                // continue working
            } else {
                // throw an error
            }
        }, 'json');
    }, 3000);
});

But this example produces a timeout for every typed character and I get about 20 AJAX requests if I type-in 20 characters.

On this fiddle I demonstrate the same problem with a simple alert instead of an AJAX.

Is there a solution for this or I'm just using a bad approach for this?


Solution

  • You'll have to use a setTimeout (like you are) but also store the reference so you can keep resetting the limit. Something like:

    //
    // $('#element').donetyping(callback[, timeout=1000])
    // Fires callback when a user has finished typing. This is determined by the time elapsed
    // since the last keystroke and timeout parameter or the blur event--whichever comes first.
    //   @callback: function to be called when even triggers
    //   @timeout:  (default=1000) timeout, in ms, to to wait before triggering event if not
    //              caused by blur.
    // Requires jQuery 1.7+
    //
    ;(function($){
        $.fn.extend({
            donetyping: function(callback,timeout){
                timeout = timeout || 1e3; // 1 second default timeout
                var timeoutReference,
                    doneTyping = function(el){
                        if (!timeoutReference) return;
                        timeoutReference = null;
                        callback.call(el);
                    };
                return this.each(function(i,el){
                    var $el = $(el);
                    // Chrome Fix (Use keyup over keypress to detect backspace)
                    // thank you @palerdot
                    $el.is(':input') && $el.on('keyup keypress paste',function(e){
                        // This catches the backspace button in chrome, but also prevents
                        // the event from triggering too preemptively. Without this line,
                        // using tab/shift+tab will make the focused element fire the callback.
                        if (e.type=='keyup' && e.keyCode!=8) return;
                        
                        // Check if timeout has been set. If it has, "reset" the clock and
                        // start over again.
                        if (timeoutReference) clearTimeout(timeoutReference);
                        timeoutReference = setTimeout(function(){
                            // if we made it here, our timeout has elapsed. Fire the
                            // callback
                            doneTyping(el);
                        }, timeout);
                    }).on('blur',function(){
                        // If we can, fire the event since we're leaving the field
                        doneTyping(el);
                    });
                });
            }
        });
    })(jQuery);
    
    $('#example').donetyping(function(){
      $('#example-output').text('Event last fired @ ' + (new Date().toUTCString()));
    });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    
    <input type="text" id="example" />
    <p id="example-output">Nothing yet</p>

    That will execute when:

    1. The timeout has elapsed, or
    2. The user switched fields (blur event)

    (Whichever comes first)