Here is the code that executes for the keyup event of a legitimate key
(excluding keys like Shift, Home, etc.):
[------------------------------------------------------------------------
function doSuggest() { getSuggest(db_table_name, db_column_name,
element(input_box_id).value, max_listings); }
timeoutID = window.setTimeout(doSuggest, 250);
--------------------------------------------------------------------------]
And here's the getSuggest function:
[--------------------------------------------------------------------------
function getSuggest(db_table_name, db_column_name, db_search,
max_listings) {
if (!isWorking && http) {
var randomNumber = Math.floor(Math.random() * 9999999);
var url = "../get_suggest.php" +
"?r=" + randomNumber+
"&db_table_name=" + db_table_name +
"&db_column_name=" + db_column_name +
"&db_search=" + db_search +
"&max_listings=" + max_listings;
http.onreadystatechange = handleHttpResponseXML;
http.open("GET", url, true);
//By the way, I've tried swapping the
above 2 lines, but this doesn't seem to make a difference.
isWorking = true;
http.send(null);
}
}//end function getSuggest
--------------------------------------------------------------------------]
Finally, here's the handleHttpResponseXML code:
function handleHttpResponseXML() {
if (http.readyState == 4) {
if (http.status != 200) {
alert("ERROR ON REMOTE SCRIPT! " + http.status);
return false;
}
if (http.responseText.indexOf('invalid') == -1 && http.status
== 200) {
var xmlDocument = http.responseXML;
var num_rows =
xmlDocument.getElementsByTagName('num_rows')[0].firstChild.data;
if(num_rows < 1) {
hide('suggest_box');
} else {//(num_rows >= 1)
var listings =
xmlDocument.getElementsByTagName('output')[0].firstChild.data;
num_listings = num_rows;
document.getElementById('suggest_box').innerHTML = listings;
show('suggest_box', 'inline');
//Reset listing_number back to 1 and highlight it.
listing_number = 1;
highlight('listing1');
//----------- TYPE-AHEAD ------------//
//If user pressed backspace or delete, don't do type-ahead.
if (key == BACKSPACE || key == DELETE) {
//Ignore.
} else {
//Show the first listing (which is the closest match) in the
//textbox, highlighting the part the user has not typed.
//First, get length of the text currently in textbox.
var charLength =
document.getElementById(input_box_id).value.length;
//Next, show the listing in the search box.
document.getElementById(input_box_id).value =
document.getElementById('listing1').innerHTML;
//Finally, highlight everything in the search box
//after what the user had originally typed:
highlightRange(input_box_id, charLength);
}//end type-ahead block
}//end else (num_rows >= 1)
isWorking = false;
}
}
}//end function handleHttpResponseXML
I guess I need to include the highlightRange function, too:
[-----------------------------------------------------------------------
function highlightRange(elem, start, end) {
var textbox = document.getElementById(elem);
charLength = textbox.value.length;
switch (arguments.length) {
case 1:
start = 0;
end = charLength;
break;
case 2:
end = charLength;
break;
}//end switch
if (textbox.createTextRange) {
var range = textbox.createTextRange()
range.moveStart("character", start);
range.moveEnd("character", end);
range.select();
} else if (textbox.setSelectionRange) {
textbox.setSelectionRange(start, end);
}
}//end function hightlightRange
------------------------------------------------------------------------]
Alright, so again, I know it's probably a synchronization problem.
I've tried using setTimeout, but this isn't going to solve the problem.
Also, I've found out exactly when the problem occurs. Using the same
example, let's say that "ja...@test.com" is the first listing that comes
up for "ja". And let's say that I'm looking for something starting
with "jas". Now, the problem occurs exactly in the following timing: I
type "ja" quickly, wait a split second and then *right before*
"ja...@test.com" shows up, I type in an "s" - but the result from typing
the "ja" ("ja...@test.com") shows up and overrides the "s" I just typed
in. Any ideas on how I can fix this problem (or synchronize the AJAX
properly)?
[snip]
> function doSuggest() { getSuggest(db_table_name, db_column_name,
> element(input_box_id).value, max_listings); }
Here you are sending the value of the text input to the getSuggest
function. This would include the lookahead that was placed into the
text input by:
[snip]
> //Next, show the listing in the search box.
> document.getElementById(input_box_id).value =
> document.getElementById('listing1').innerHTML;
So either don't put the entirety of the suggestion into the text input
or don't send the selected portion of the text input to the getSuggest
function.
> Alright, so again, I know it's probably a synchronization problem.
Nope. It's doing exactly what you're telling it to. It doesn't have
anything to do with the AJAX. OT: You should consider using key events
instead of a timer. There's no need to make a roundtrip to the server
if the user hasn't typed anything.
1) User types a letter.
2) On keydown, the highlighted type-ahead text disappears so that now
all that appears is what the user has typed.
3) On keyup, the autosuggest function is called:
getSuggest(db_table_name, db_column_name,
document.getElementById(input_box_id).value, max_listings).
4) The XmlHttpRequest object is used to query the database with the
current value of the input box (which *should*, at this point, be only
what the user has typed (see step 2).
5) Results come back from this query, populating the drop-down suggest
box and populating the input box with the first result, highlighting
what the user has not typed.
I did not look your code through (it's big :-)
Just for hell of it:
instead of:
var randomNumber = Math.floor(Math.random() * 9999999);
try:
var randomNumber = (new Date()).getTime();
It would be helpful to see all the code and the markup involved. Do you
have it uploaded somewhere? Otherwise we are making suppositions about
things we cannot see. I assumed you were using a timer but you are
using key events.
Right, of course. I've copied it over to my personal school webspace.
So here's the address: http://web.njit.edu/~jmb2/Joel/suggest.php
OK. Some testing shows that the network lag is causing the poor
behavior. I type "ang" quickly and then it was replaced with "Aaron
Patterson" with the "on Patterson" selected. So the result was from the
first "a" typed. But it still respected the fact that I had typed 3
characters when it did the selection routine.
I think you can solve the issue if you'll cancel all previous requests
on keydown. Then what the user types will never be replaced and only
the last keyup will be an active request. Thoughts?
Most xmlhttp implementations have an "abort" method which you could
call to cancel the processing. Just keep the previous request
referenced by a variable and call the abort method on that variable
when a new request is made. Should be simple...
arrange to ignore the data it returns
or cancel it's onReadyStateChange handler
there may even be a way to close it.
but first you'll need a way to address it.
--
Bye.
Jasen