Reducing the number of AJAX calls

Hi

I have a search box which uses AJAX to query a database and return a list of suggestions to the user, it works off the “onkeyup” event on the input textbox.

 <input type="text" id="txt1" onkeyup="showHint(this.value)" />

Obviously this creates an AJAX call for every keypress the user makes and I’d like to make this more efficient by adding a short delay before the AJAX call is made.

How would I modify my code to make this happen?

JavaScript code below:

<script type="text/javascript">

function showHint(str)
{
var xmlhttp;
if (str.length==0)
  { 
  document.getElementById("txtHint").innerHTML="";
  return;
  }
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    document.getElementById("txtHint").innerHTML=xmlhttp.responseText;
    }
  }
xmlhttp.open("GET","gethint.asp?q="+str,true);
xmlhttp.send();
}
</script>

Thanks

Not tested but try:

<script type="text/javascript"> /* Anywhere BELOW the form element */

document.getElementById( "txt1" ).onkeyup = (function()
{
   var tm;

   return function()
   {
     clearTimeout( tm );

     tm = setTimeout( function(){ showHint(this.value), 1000 }
   }

})();

</script>

Another thing you could add is to only start a search after two or three characters have been entered. That, combined with Logic Ali’s suggestion above should significantly reduce the number of requests.

Yeah that’s a great idea and I looked at my code and it was really obvious to implement:

<script type="text/javascript">

function showHint(str)
{
var xmlhttp;
[COLOR="Red"][B]if (str.length<3)[/B][/COLOR]
  { 
  document.getElementById("txtHint").innerHTML="";
  return;
  }

Sadly though Logic Ali’s code didn’t seem to work, not in Firefox at any rate. :frowning: Any other suggestions?

If you’d used the error console you probably could have fixed the error:

document.getElementById( "txt1" ).onkeyup = (function()
{
   var tm;

   return function()
   {
     clearTimeout( tm );

     tm = setTimeout( function(){ showHint(this.value) }, 1000 );
   }

})();

The misplaced bracket? Yeah I saw that, but even with the correction I get an error which throws back to the original script… it looks like some kind of incompatibility between your script and my implementation of Immerse’s suggestion to wait until at least 3 characters were typed.

Message: ‘length’ is null or not an object
Line: 14
Char: 1
Code: 0

I don’t get this error without your delay code in place but its referencing the following line in the main script which makes the Ajax call.:


if (str.length<3)

Should add, I appreciate your help with this, Javascript has never been my strong point.

<DOMNode>.value probably isn’t an instance of the String object, and as such doesn’t have a ‘length’ property.

Try changing


if (str.length < 3) { ... }

to


if (String(str).length < 3) { ... }

That seems to implement the delay but messes up the Ajax call, I get no matches on my Ajax query where before I had them. It also seems to cause the Ajax call to happen instantly after 1 second in Internet Explorer, even if no keypress is made.

It executes because of the closure, which fires the function immediately on creation, even when no key has been pressed yet. Remove the stuff in red:



document.getElementById( "txt1" ).onkeyup = [color="red"]([/color]function()
{
   var tm;

   return function()
   {
     clearTimeout( tm );

     tm = setTimeout( function(){ showHint(this.value), 1000 }
   }

}[color="Red"])()[/color];

I don’t know why the AJAX call is messed up though. How is it messed up?

That fixes the IE instant execution, but I still have the problem with my Ajax call, since I put in the “String(str)” change you suggested, it just doesn’t seem to make the call at all.

Its as if the following condition is always true even though I have typed 3+ letters into the search box.

if (String(str).length<3)
 { 
  document.getElementById("txtHint").innerHTML="";
  return;
  }

Or I suppose it could be that the timer is not clearing, (those are milliseconds right?) either way I’m just getting nothing from my ajax call even though previously I was.

It’s a scope issue which this should fix:

document.getElementById( "txt1" ).onkeyup = (function()
{
   var tm;

   return function()
   {
     var obj = this;
       
     clearTimeout( tm );

     tm = setTimeout( function (){ showHint( obj.value) }, 1000 );
   }

})();

Genius! Thank you very much for your help!:slight_smile:

Sorry to bounce this thread, but I have another query.

I know we spent a lot of time on this example reducing the number of AJAX calls so there was no more than 1 per second and only after at least 3 characters had been typed into the search box.

But I now want to hotlink to the page that makes this AJAX call with a pre-defined search parameter in the QueryString, so I need to the script to make the Ajax call as the page first loads (but still only if the search parameter contains at least 3 characters) as well as when there is a change to the search textbox.

I tried adding:

<body onload=“showHint(<%=Request.QueryString(“q”)%>);” >

But this doesn’t work.

Can anyone shed any light on this or am just being dense?

Scrap that I was being dense… I just needed to put quotes around the Querystring parameter I was pushing to the showHint() function. Duh!