Reputation: 555
I am trying to make an instant search drop down for my site. All work fine, except for this.
var timeOut;
$('#search input[name=\'search\']').on('keyup', function(e) {
// If enter - submit the search field
if (e.keyCode == 13) {
$('header input[name=\'search\']').parent().find('button').trigger('click');
}
// Call only when length is at least 2 and the key pressed is alphanumeric
else if ($('#search input[name=\'search\']').val().length>2 && ((e.keyCode>=65 && e.keyCode<=90) || (e.keyCode>=97 && e.keyCode<=122))) {
timeOut = null;
//alert(timeOut);
if (!timeOut) {
timeOut = setTimeout(function() {
$.ajax({
url: 'ajax.php',
type: 'post',
async: false,
data: 'ACTION=SEARCH&search='+$('#search input[name=\'search\']').val(),
dataType: 'json',
beforeSend: function() {
$('#loader-icon').show();
},
complete: function() {
$('#loader-icon').hide();
},
success: function(json) {
//$('.product-list-row').html(json);
$('#search-listing').html(json['html']);
},
error: function(xhr, ajaxOptions, thrownError) {
alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
});
timeOut = null;
}, 500);
}
}
});
Problem 1: My script ends up making too many calls to the server, for some reason setTimeOut
isn't working as I expected it to. Ajax call should only be made when the user has done typing or something like that and not at every key press.
Problem 2: For some reason when I type fast the input bar doesn't get edited. The ajax however works, but with the last textual input.
Upvotes: 2
Views: 1763
Reputation: 15846
Instead of trying to tackle this with setTimeout
, you can abort the previous call using the jqXHR
object returned by the AJAX. It is more clean and simple to use this. Remove async: false,
too.
var timeOut;
var xhr;
$('#search input[name=\'search\']').on('keyup', function(e) {
// If enter - submit the search field
if (e.keyCode == 13) {
$('header input[name=\'search\']').parent().find('button').trigger('click');
}
// Call only when length is at least 2 and the key pressed is alphanumeric
else if ($('#search input[name=\'search\']').val().length>2 && ((e.keyCode>=65 && e.keyCode<=90) || (e.keyCode>=97 && e.keyCode<=122))) {
if(xhr && xhr.readyState != 4){
xhr.abort();
}
xhr = $.ajax({
url: 'ajax.php',
type: 'post',
data: 'ACTION=SEARCH&search='+$('#search input[name=\'search\']').val(),
dataType: 'json',
beforeSend: function() {
$('#loader-icon').show();
},
complete: function() {
$('#loader-icon').hide();
},
success: function(json) {
//$('.product-list-row').html(json);
$('#search-listing').html(json['html']);
},
error: function(xhr, ajaxOptions, thrownError) {
alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
});
}
});
Upvotes: 2
Reputation: 4142
The right solution is combination of both, abort running request, if new one should be made, and also, tackle firing of the new request. You can use underscore library, which has nice function for that, called debounce (see http://underscorejs.org/#debounce) and your code should looks like this:
// This is your xhr, each request save into this
// variable, in order to be able to abort it if needed
var xhr;
// Wrap your event handler using the debounce function
$("#search").on("keyup", _.debounce(function(e) {
// Abort running request
if(xhr) {
xhr.abort();
xhr = null;
}
// Store the new request
xhr = $.ajax({
// Search for the term $(this).val()
});
},500));
There is no need to fire the search for each keyup, but only when user stopped typing - debounce will do it for you. And there is no need to handle previous results if the request should be made.
Upvotes: 2