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.
$.ajaxreturns ajqXHRobject and that object exposes theabortmethod of the underlyingXMLHttpRequestobject. So, you just need to stash thejqXHRand abort the request at the right time. Something like this perhaps:And then you’d want some stuff on
#city: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
$.ajaxrequest 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 theblurhandler and cancelling the timer in afocuscallback (you could store the timer-ID in another.data()slot too).I haven’t tested this but I’m pretty sure it will work.