4

I'm trying to build a simple autocomplete list:

DOM:

<input id="example"/>
<div id="results"></div>

Javascript:

$('#example').keyup(function(e) {
    $('#results').empty();
    $.getJSON('Search?input=' + $('#example').val(), function(response) {            
          // This attaches the results to the results div
          updateAutocomplete(response);
    });
});

This works, except as the user is typing, I might receive the callbacks in different order. Is there anyway around this? I thought about attaching a timestamp to the results and doing a quick comparison (that way if an earlier response comes later, it'll get rejected). This must be a common problem, what's the best way around this?

chum of chance
  • 6,200
  • 10
  • 46
  • 74

4 Answers4

2

You can store and cancel the previous request as you go, like this:

var xhr;
$('#example').keyup(function(e) {
  $('#results').empty();
  if(xhr) {
    xhr.abort();
    xhr = null;  //cleanup
  }
  xhr = $.getJSON('Search?input=' + $('#example').val(), function(response) {
    updateAutocomplete(response);
  });
});

$.getJSON() returns the XmlHttpRequest it creates, so we're just hanging onto a reference to it and aborting if needed.

Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
  • +1, although variables created with *var* cannot be deleted. *delete* is for object properties and implicitly declared variables. `xhr = undefined` would be more appropriate. – Andy E Aug 31 '10 at 22:49
  • @Andy - doh, you're right of course, I was taking this from a widget I had made here and didn't remove all the tidbits, good catch. – Nick Craver Aug 31 '10 at 23:11
1

A delay will be helpful. So, lets see what this posts says: jquery keyup delay?

var delay = (function(){
  var timer = 0;
  return function(callback, ms){
    clearTimeout (timer);
    timer = setTimeout(callback, ms);
  };
})();

Usage:

$('input').keyup(function() {
    delay(function(){
      alert('Time elapsed!');
    }, 1000 );
});

(code by CMS)

Community
  • 1
  • 1
Garis M Suero
  • 7,974
  • 7
  • 45
  • 68
  • I found this stackoverflow answer a while back and have been using this delay function for some time now. Unfortunately, I just discovered that if you call delay() multiple times, the previous calls are overwritten and never triggered. See example jsfiddle. "Do thing 1" is never executed. http://jsfiddle.net/yxBBF/1/ – mrbinky3000 Jun 27 '12 at 14:48
  • Answered my own question. I tweaked this pattern so you can use it for anything, not just key up, and attach multiple events without previous events getting overwritten. http://jsfiddle.net/mrbinky3000/seh97/26/ – mrbinky3000 Jun 27 '12 at 16:31
0

I've typically opted for a few things...

  1. have a small delay before sending (200-250ms)... (e.g. queue the queries) if another keystroke comes in, ignore the old queries.
  2. store the latest keyword you are querying on... in your results, be sure to return the keyword and before displaying ensure that the keyword matches the latest.
scunliffe
  • 62,582
  • 25
  • 126
  • 161
0

Just use closure for callback function with incrementing identifier for each callback instance. Something like this:

var id = 0;
function next_autocomplete_handler() {
  var handler_id = ++id;
  return function(response) {
    if (handler_id == id)  // is this latest latest?
      updateAutocomplete(response);
  };
}

$('#example').keyup(
  function(e) {
    $('#results').empty();
    $.getJSON('Search?input=' + $('#example').val(),
              next_autocomplate_handler());
  });
Vadim Shender
  • 6,703
  • 1
  • 16
  • 10