Thursday, June 22nd, 2006

Autcompletion Issues with Yahoo, Scriptaculous Libraries

Category: Remoting, Scriptaculous, Toolkit, Yahoo!

<>p>Cheng Guangnan reports on a potential issue with the autocompletion/suggestion support offered by both Yahoo UI and Scriptaculous libraries. The problem involves parallel calls – there’s the potential for an initial list of suggestions to be displayed after a subequent list. His screencasts show what’s going on.

1. “2006” is typed.
2. A request of “2006” sent to the server.
3. User continues typing and now “200607” typed.
4. Another request of “200607” sent to the server.
5. User waiting for feedback.
6. The second request return, it show the popup.
7. The first request return, it show the popup with data returned for “2006”.

If that’s the case (and we haven’t verified it!), the problem could be solved by some form of Call Tracking. If the first call comes back after the second, simply discard it.

Related Content:

Posted by Michael Mahemoff at 5:26 am
29 Comments

++---
2.9 rating from 15 votes

29 Comments »

Comments feed TrackBack URI

Kind of reminds me of Apple’s annoying Spotlight search feature which is nearly useless on anything but the fastest machines. I am not sure exactly how these libraries are working but I think that coding in a delay of two to three seconds after the users last keypress would be a good way to deal with this that would also help reduce the load on the server.

Comment by Twist — June 22, 2006

Twist, you can see an example of that (adding a delay) at http://www.ajaxify.com/tutorial/. The “Streamlining Performance” step reduces a similar kind of error, in which results get merged from two different queries. A delay can be useful, but still not all that robust – the first call still might get stuck somewhere and take a few more seconds to process.

Comment by Michael Mahemoff — June 22, 2006

How about a patch…? :)

Comment by Thomas Fuchs — June 22, 2006

I think a callstack like this is implemented in, for example, HTML_AJAX – a PHP package found in PEAR.

Comment by Markus Wolff — June 22, 2006

typo.. “after the second” ?

Comment by sylvain — June 22, 2006

See my demo at http://munich.schwarz-interactive.de/autocomplete.aspx, do you get the same problem there? I couln’t get a “wrong” result.

Comment by Michael Schwarz — June 22, 2006

It should be noted that he is selling a solution for $50 to the ‘problem’ in these libraries.

Comment by Phil — June 22, 2006

Autcompletion Issues with Yahoo, Scriptaculous Libraries

Cheng Guangnan reports on a potential issue with the autocompletion/suggestion support offered by both…

Trackback by Ajax.NET Professional — June 22, 2006

This seems like an easy problem to fix if it does exist. With every request send an incrementing number. Have the response send back that number. If your receive a number that is less than a number you have previously received just discard the request. Networking protocols have been doing this for ages.

Comment by Eric Anderson — June 22, 2006

Yes, this would be an idea. I’m currently not allowing to start another request while one is running.

Comment by Michael Schwarz — June 22, 2006

Wasn’t there a talk about this at this year’s PHP conference in London?
It’s hardly a revelation…

And the preview thing on these comments (I assume…) means letters don’t appear for a good 5 seconds after I’ve typed them…rather annoying.

Comment by G Jones — June 22, 2006

Is it ever useful to have a list of search results for a string that isn’t the value of the input field any more? I haven’t ever used Autocomplete, so I don’t know. But if it isn’t, then surely the callback handling the Ajax request could just check to see if the text in the input field is the same as the string that was sent to the server?

In which case step 7 would become: the first request returns, but does nothing because the text in the input field is no longer “2006″.

Comment by Aanand — June 22, 2006

How about calling abort() on the XMLHttpRequest object before sending the second request?

Comment by Jonathan Leech — June 22, 2006

Why call again to the server?

If the last query is the beginning of the new search query, so we can just filter the last search result and show the filtered list.
Else, call again to the server.

Comment by Nir Tayeb — June 22, 2006

Eric, indeed that’s what I was getting at with Call Tracking. Also, you don’t actually have to transfer the number back and forth if you don’t want (ie you can leave the server-side alone); just associate an incrementing call ID with the XHR itself. When the response comes back, the handling XHR looks at its own call ID and decides whether it’s the newest XHR to be used so far.

Comment by Michael Mahemoff — June 22, 2006

I encourage you guys to check this one out too:
http://developer.ebusiness-apps.com/technologies/webdevelopment/codeandcomponents/ebawebcombov3/media/demos.htm

Comment by alexei — June 22, 2006

@Thomas: I agree with you in spirit, but I’m pretty sure this guy is just out to hawk his wares. He’s been spamming all Ajax and web-dev related mailing lists lately. It actually looks like he ran yours on a slower machine to make his product look better.

Comment by nate — June 22, 2006

Nate, you can’t say I’m not correct because of I do spam.

Sorry for spam you. :P

I record the screencast on my AMD Althon 2000+ with 1G memory. I made it *played slowly than the actuly speed* to make sure that everyone could see what happen clearly.

Comment by chenggn — June 22, 2006

Did someone test the suff from Michael? Where can I get this control? (Sorry, I’m .NET developer).

Comment by Oliver — June 22, 2006

An easy fix for the Yahoo libraries is to modify _populateList. If the query does not currently match whatever is in the text box, just ignore it. It works quite well for me.

if (sQuery != oSelf._oTextbox.value) { return; }

Comment by Scott — June 22, 2006

I found a workaround to this for Scriptaculous using the onLoading option.


// global var
var my_search;

// option for Ajax.Autocompleter
onLoading: function(request)
{
// kill the last search that might be pending
try { my_search.abort(); } catch (e) {}

my_search = request;
}

Comment by zac — June 22, 2006

This problem does exist, and once you see it, it takes 10 minutes to fix, if that. I don’t know if it warrants this much attention though.

Comment by Andy — June 22, 2006

I’ve been working this problem for a while. This is also a problem when you use Prototype to build and track your ajax request/responses. You will need to build some sort of call stack mechanism and return a parameter with your response that helps you match it up with the request.

Comment by Ram — June 23, 2006

It should be noted that he is selling a solution for $50 to the ‘problem’ in these libraries.

Ugh…

Comment by Matthew Ratzloff — June 23, 2006

Isn’t this simply one of the issues with using async requests? Your responses may come back in a different order than the requests were went. Using a sequence sent with each request and returned by a response will help, but only if the UI state of the suggestion box is orthogonal to any other page state.

Comment by Ted — June 23, 2006

Its not that hard to slove, my guess would be just to Abort the Ajax call, when trying to send a new Ajax call on key up.

1. User enters 2006
2. (on key up) Ajax Call, find match for 2006 results query
3. user types 200698 or whaterver.
4. abort #2 query, ajax call
5. Ajac call for #3
6. Loop if needed.

All the librarys do support some method of abort() for ajax requests,

right? or no?

Comment by Nilesh — June 23, 2006

The simplest way is to only allow 1 request pending at anytime. If a higher priority request is required, pending requests can be aborted.

Comment by wei — June 24, 2006

For script.aculo.us just use Form.Element.DelayedObserver in Autocompleter (controls.js):

Wrong way (as you can see in controls.js):
Event.observe(this.element, “blur”, this.onBlur.bindAsEventListener(this));
Event.observe(this.element, “keypress”, this.onKeyPress.bindAsEventListener(this));

Right way:
Event.observe(this.element, “blur”, this.onBlur.bindAsEventListener(this));
new Form.Element.DelayedObserver(this.element, 0.5, this.onKeyPress.bindAsEventListener(this));

Comment by Vital — August 15, 2006

In prototype 1.5.0_rc1 and scriptaculous 1.6.4 we had this problem and in 9.5 minutes we had a solution.

To the definition of Ajax.Request in prototype.js we added the function:

abort: function() {
this.transport.abort();
}

In Scriptaculous (controls.js) Ajax.Autocompleter:
we declared a global variable:

var my_search;

we updated getUpdatedChoices() so the last two lines in the method look like:

//line added
try {my_search.abort();} catch (e) {}
//line changed
my_search = new Ajax.Request(this.url, this.options);

Thanks to zac for leading us down the right path.

Comment by Matt Reed — November 29, 2006

Leave a comment

You must be logged in to post a comment.