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.
$.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With