None

Moving Away from AJAX

April 25, 2010

I had some fields on my site which used ajax. These would look at what the user was typing in, and for each keystroke would do a request to the server for a list of matches. These matches would then be shown below the field in a popup list.

Old Approach

Here's the JavaScript code involved:

function lookup(inputString,pType){
if(inputString.length == 0) {
    // Hide the suggestion box.
    $('#suggestions').hide();
} else {
    $.post("/addresults/rpc/"+pType+"/", {q: ""+inputString+""}, function(data){
        if(data.length > 0) {
            $('#suggestions').show();
            $('#autoSuggestionsList').html(data);
        } else{
        $('#suggestions').hide();       
    }
    });
  }
}

function fill(thisValue,pType) {
    $('#' + pType).val(thisValue);
    $('#suggestions').hide();
}

Here's the HTML:

<input type="text" id="testpiece" name="TestPiece" onkeyup="lookup(this.value,'testpiece');" maxlength="100">
&nbsp;&nbsp;<input type="submit" value="Submit" />
<div class="suggestionsBox" id="suggestions" style="display: none;">
  <img src="/site_media/images/autoComplete/upArrow.png" style="position: relative; top: -12px; left: 30px" alt="upArrow" />
  <div class="suggestionList" id="autoSuggestionsList"></div>
</div>

The problems with this approach were:

  • Each keystroke was a request to the server, increasing the load
  • Sometimes the responses came out of order, especially if the user typed quickly

In this situation, I had a finite list that was less than 2000 items long, so I opted to rework the code.

New Approach

This new method includes the entire list into the page at render time as an JSON variable. This list is then iterated and the ones that start with what has been typed are shown in the popup list. In order to improve performance the data is fetched using a SQL query for just the single field required, rather than a Djano ORM request for the entire object.

Here's the code to build the JSON variable

lData = [{% for result in Data %}{ "name" : "{{result}}" }{% if not forloop.last%},{%endif%}{% endfor %}];

Here's the JavaScript:

function startsWith(pLookIn, pLookFor) {
  lRegEx = new RegExp("^"+pLookFor, 'i')
  lMatch = pLookIn.match(lRegEx)
  if (lMatch) {
    return lMatch[0].toLowerCase()==pLookFor.toLowerCase();
  } else {
      return false;
  }
}

function showSuggestionBox(inputString){
  lHtml = ""
  for (var i=0; i<lData.length; i++) {
    if (startsWith(lData[i].name, inputString)) {
      lHtml += '<li onclick=\'fill("'+lData[i].name+'");\'>'+lData[i].name+'</li>\n';
    }
  }

  if (lHtml.length > 0) {
    $('#suggestions').show();
    $('#autoSuggestionsList').html(lHtml);
  } else {
    $('#suggestions').hide();
  }    
}

function lookup(inputString)
{
  if(inputString.length == 0) {
    $('#suggestions').hide();
  } else {
    showSuggestionBox(inputString);
  }
} // lookup

function fill(thisValue) {
    $('#contest').val(thisValue);
    $('#suggestions').hide();
}

And finally, here's the HTML:

<input onkeyup="lookup(this.value);" id="contest" type="text" name="contest" maxlength="100" value="{{value}}"/>
&nbsp;&nbsp;<input type="submit" value="Submit" />
<div class="suggestionsBox" id="suggestions" style="display: none;">
  <img src="/site_media/images/autoComplete/upArrow.png" style="position: relative; top: -12px; left: 30px" alt="upArrow" />
  <div class="suggestionList" id="autoSuggestionsList"></div>
</div>

Tags: ajax json popup django