views:

34

answers:

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?

A: 

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)

Garis Suero
+2  A: 

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
+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
@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
A: 

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
A: 

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