Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jquery ui autocomplete: how to cancel slow ajax request after text input loses focus

I'm using an JQuery UI Autocomplete field that is tied to an ajax lookup that can be rather slow at times. Occasionally a user will tab away from the text input field after the ajax query initiates but before the ajax call returns. When this happens, the autocomplete pop-up appears even though the text input no longer has focus, and the only way to dismiss the popup is by selecting an item (rather than tabbing to another field).

In fact, this jquery ui demo exhibits the same behavior (for example enter 'ariz' into the text field, wait for the 'searching' animation to appear and then tab out of the field before it returns any results).

One solution that works (but feels like a hack) is to check in the ajax's success callback to see if the text field still has focus, and if not to call response() with an empty list, for example:

        $( "#city" ).autocomplete({
        var elem = this.element;
        source: function( request, response ) {
            $.ajax({
                url: "http://ws.geonames.org/searchJSON",
                data: {name_startsWith: request.term},
                success: function( data ) {
                    if(!$(elem).is(':focus') {
                        response({});
                        return;
                    }
                    response( $.map( data.geonames, function( item ) {
                        return {
                            label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
                            value: item.name
                        }
                    }));
                }
            });
        }
    });

Is there a more graceful solution? Ideally I'd like to cancel the ajax request as soon as the user tabs away from the text input field.

like image 375
NobodyMan Avatar asked Oct 13 '11 01:10

NobodyMan


1 Answers

$.ajax returns a jqXHR object and that object exposes the abort method of the underlying XMLHttpRequest object. So, you just need to stash the jqXHR and abort the request at the right time. Something like this perhaps:

source: function(request, response) {
    var $this = $(this);
    var $element = $(this.element);
    var jqXHR = $element.data('jqXHR');
    if(jqXHR)
        jqXHR.abort();
    $element.data('jqXHR', $.ajax({
        // ...
        complete: function() {
            // This callback is called when the request completes
            // regardless of success or failure.
            $this.removeData('jqXHR');
            // And dismiss the search animation.
            response({});
        }
    });
}

And then you'd want some stuff on #city:

$('#city').blur(function() {
    var $this = $(this);
    var jqXHR = $(this).data('jqXHR');
    if(jqXHR)
        jqXHR.abort();
    $this.removeData('jqXHR');
});

There is a brief blur/focus sometimes during the autocompleter's normal operation but that only happens when when someone chooses something from the list; there shouldn't be a $.ajax request running when that happens so we don't need to worry about it. If I'm wrong then you can kludge around it by using a timer inside the blur handler and cancelling the timer in a focus callback (you could store the timer-ID in another .data() slot too).

I haven't tested this but I'm pretty sure it will work.

like image 54
mu is too short Avatar answered Oct 06 '22 05:10

mu is too short