abhig10
abhig10

Reputation: 555

Reduce number of ajax calls in instant search

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

Answers (2)

rrk
rrk

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

xxxmatko
xxxmatko

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

Related Questions