AJAX Suggestions – a new JavaScript library for you
Updated April 10th with a couple of features per comment suggestions
Ok, I’ve added a couple of features and settings based on the comments I’ve got. The additional settings are:
hideResultsOnDocumentClick
- If a click on the
document
should hide the results list. itemInsertValueSetFocusToInput
- If the focus should be set to the
input
element, once an item has updated theinput
element’s value (by initially having theitemInsertValueIntoInputClassName
CSS class). hideResultsWhenInsertValueIsSelected
- If the results list should be hidden when the
input
element’s value is updated.
Another additional feature is that if you use the arrow keys to navigate the results list, arrow up from the first item as well as arrow down from the last, will set the focus back to the input
element.
Updated April 11th
Changed the license to a Creative Commons Deed.
Updated April 13th
I got an e-mail with the suggestion to turn off the web browser autocompletion feature of the input
element. I think it was a good idea, so I have now added it as a setting:
turnAutoCompleteOff : true
When most people need some kind of intellectual challenge, they do crosswords, sudokus, IQ tests, read up on philosophy or something similar. Me? I write some JavaScript. π
The thing is, though, that my small hobby scripts soon turn into something I think will be useful for other people, so I feel a strong urge to share it. Branded by having web developing as a living, after the initial rush and the proof of concept phase, more “sexy” matters come into my mind: scalability, performance, cross browser and cross platform support etc…
Anyway, once I’ve spent way too much time on that (and since I want to be a good father and husband, we’re talking late nights; basically, anytime when I seem to be the only person in the entire world awake), sooner or later I reach I time when I’m ready to share. Luckily, for my sanity, that time is now.
Background
I think that for me, as well as many other web developers, the spark was lit when I saw Google Suggest for the first time. Since then, I’ve wanted to (re-)create that JavaScript since I think it’s a perfect example of good progressive enhancement; helping the end user, but it’s nothing vital that the whole web site depends on.
I was kind of hoping that a project would turn up where this was asked for, but it never came (at least not yet), so I had to give in and create the script in my free time. The good thing with that, however, is that I own the script and can bring it to any project I seem fit. I just hate leaving my “script babies” behind. π
The script and the concept
The result of my efforts is AJAX Suggestions!
The general idea is that, as soon as you type a couple of characters into an input
element, a JavaScript makes an AJAX call to the server, which performs a search and returns the results/suggestions and displays them adjacent to the input
element. The user can then click on, or navigate through the results with the keyboard, and choose the item they prefer.
I’ve also added some functionality to use this to do a look-up of the value in the input
element, and when one of the results are chosen, it replaces the value in the input
element with the value of the result. This can be useful, for instance, in a web-based e-mail application where you want to auto-complete recipients. No doubt, many will want to tweak this for their specific needs, and that’s just fine. My advice, however, is to override the methods in an external JavaScript file, in case I update the core AJAX Suggestions JavaScript file.
Implementation
The script looks for elements that have the CSS class ajax-suggestion
and then another CSS class, like url-the-actual-URL
, where you should replace the-actual-URL with your desired URL for the AJAX call. The HTML code can look something like this:
<input type="text" class="ajax-suggestion url-ajax-demo.php">
Important to note is that AJAX Suggestions supports multiple elements in the same web page, who can also use different URLs for their AJAX calls. The same element is used for presenting the results, so it will only show you the results/suggestions for the latest search.
A library, in two versions
I’ve created two versions of this script. One that is completely stand-alone, and will automatically work as soon as it’s included into a web page (given that the element/-s contain the desired CSS classes, of course.)
The other one is written based on DOMAssistant, which means that is slightly smaller (and leaner, according to my taste).
License and copyright
AJAX Suggestions is released under a Creative Commons Deed license. Basically, it means that it’s free to use and improve, and that the JavaScript files keep their reference to its creator. Like this:
/*
AJAX Suggestions is developed by Robert Nyman, http://www.robertnyman.com, and it is released according to the
Creative Commons Deed license (http://creativecommons.org/licenses/GPL/2.0/)
For more information, please see http://www.robertnyman.com/ajax-suggestions
*/
Where to go from here
Go to the AJAX Suggestions mini web site, where you can see a demo of AJAX Suggestions or read through the implementation page to find about all the available settings.
You can also directly download the stand-alone JavaScript file (10 kb) or the DOMAssistant-based JavaScript file (7 kb) and start playing! π
Note: remember to call the init()
method of the ajaxSuggestions
object, with the approach you find suitable, when using the DOMAssistant version.
A thanks to PPK
In the darkest hour of developing this, I was eating candy till I felt sick and drinking about one glass of water a minute out of pure exasperation. At that time, one certain person’s work with the ins and outs of the abort
method really saved the day.
So, Peter-Paul Koch, thank you for your superb XMLHTTP notes: abort() and Mozilla bug article.
PS. You can also see it in use for the search input
element of this very web site. π DS.
One small thing I'd like to change:
1) When I tab through the links I first have to go through all the links in the right menu. Is this because you add the dropdown to the bottom? I'd rather tab directly to it from the input (tested in Firefox).
3) When I press enter on one of the list items the value gets copied, but I'd like to submit that word, so shifting focus to the input after that would be preferable. Enter #1 – fills the input, Enter #2 – submits that input contents. Tabbing backwards to the input again is just too cumbersome. Perhaps "close" all suggestions too?
2) One of your examples uses the bold tag π
Emil,
1. The idea (although I might reconsider this) is to not hijack the <kbd>tab</kbd> at all, but instead use the down arrow to set focus to the results list (the same approach as Google uses).
2. Most definitely! And it's there since it's valid, and also because there's a vast difference between the <code>b</code> and the <code>strong</code> element. Semantics, young padawan, semantics. π
3. That is a very good suggestion. My thinking was that you might want to add more than one, and therefore kept the focus in the results list but that's something I might have to revise.
My bad. I didn’t explain the highlighting carefully. I agree the highlighting of the closest matching result is not a good idea. What I meant and what the pattern also suggests is to visually distinguish the matched search term in the suggestion list.
For example, when you search for: ‘java’, the suggestion list looks like:
– javascript
– javabeans
– javalicious
Ah, I didn’t see the caching. Forgive me, I should have looked better at the actual code. It’s still early and I shouldn’t be commenting π
Haha, that is awesome. Last thursday I spend all day writing this in Javascript. Could have used this to get a good idea how to approach the problem. Some functionality I added:
– Closing the suggestion list when clicking elsewhere
– Highlighting of matched search term (see Yahoo!'s Auto Complete Design Pattern)
– As well as the suggestion mentioned above.
Difference for me is that I give suggestions to a very consistent set of data. So, I only send a xmlHttpRequest for the first character, store the results and then filter that list for the following characters, until it detects a new word.
Jeroen,
Right, missed to reply to Emil's point about close the suggestions. That's not functionality added through this library, but something each and everyone can implement in the reply they choose to deliver.
When it comes to clicking: I though about adding a click to the document to hide it, but refrained from it for some reason. Maybe I should add it, it's not a big thing to do.
Not to sure about the higlighting. To me, highlighting means that the element has focus, and you can press <kbd>enter</kbd> to choose it, arrow down, to choose the one below etc. To me, this is not combinable with keeping the focus in the <code>input</code> element to be able to continue typing.
However, a suggestion is to use another color for the first item in the returned results, and then, if desired, add functionality in a custom method to remove it.
I guess the most common usage is to do a search for each character. What I do here, though, is that I cache requests/replies, so I never look up the same term twice.
Jeroen,
Ah, sorry, I didn't read carefully enough. That's a good idea about highlighting, but definitely something I would completely leave to the result generating logic.
A downside with that, though, is that when doing a search here in my web site, it returns the title of the post matching the search criteria. This means that the post contains the search term, but, most likely, the post title doesn't so the highlighting is in vain.
One essential thing to point out, is that there is no easy way to gain focus of the input again when you've arrow-key'ed yourself on to the suggestions (only through tabbing, I believe.)
A 'Close this list/these suggestions' selection isn't very intuitive, so I suggest that when you arrow-key yourself off the list (going up when at the top, and down when at the bottom) sets focus to the input again. This is how Firefox works (don't know about other browsers,) and It feels very intuitive.
Kenneth,
That's a great idea! I've added it to the script.
Actually a CC license is not meant for software. (read about it in their FAQ!) This is a good alternative: http://creativecommons.org/licenses/GPL/2.0/
Robert, I have to admit something, the parts in your blog that contain Ajax, was generally skipped by me. But.. I'm tired, I love your blog, and I just ate so much chocolate left from Easter I'm feeling sick π So.. what better remedy than to read what you're up to today π
Thanks for sharing your "background" info, 'cause now I understand (a bit) what you're up to those late nights. Funny is I've seen those drop down suggestions on other sites too, and wondered "how the heck do they do that?"
When I have more time, I'm going to try to read the rest of your JS entries. Although I also admit, I've never really coded much in JS.. only whatever was necessary I guess.
Thanks for your hard work!
Paul,
Thanks for pointing that out! I had missed that, but now I have changed the license to the one you suggested.
Pat,
Ah, that's just fine. I'm very well aware that not everything I write is interesting to everyone. As long as write something that makes you come back, I'm happy. π
And thanks for reading!
Robert,
first of all, thanks for all your effort!
Just wanted to comment on something I came across while testing your AJAX Suggestions library in IE7.
When you click on a suggestion item in IE7, the entire URL is copied into the input field. For instance, if you click the bottom suggestion item in the demo on your site ('Click/press Enter on this, and it will add that value to the input element.') the IE7 input field value is 'www.robertnyman.com/ajax-suggestions/robertn;' while the same action in Firefox will result in the desired 'robertn;'
Any thoughts on this are greatly appreciated.
All the best,
Lionel.
Lionel,
Hmm…
I haven't access to IE 7 to test right now, but I guess it's IE 7 that automatically adds the entire domain path to the <code>href</code> attribute, unless it's preceded by something like a <code>#</code>.
My recommendation is to either use a full path or one with a <code>#</code>. Another option is to tweak the <code>insertValueIntoField</code> method of the <code>ajaxSuggestions</code> object to trim out unwanted parts.
To avoid the IE7 thing you could probably just stick the value you want in an attribute that html doesn't do anything with. "value" would be a good choice.
Anyhow, this is pretty nifty. I had to tweak it a little to get it to recognize textareas as well as input, but after that it worked quite excellently.
Erik,
Yes, that might be a good approach to it.
Good that it worked out for you tweaking it with <code>textarea</code> elements!
Now that I look at it again (I'm having to rewrite my code to take full advantage of this,) there are several options that would be better set per-element with custom attributes. The query url would probably be better as query_url="", for example.
Erik,
Personally, I don't condole of custom attributes, since its invalid HTML code and there's really no way to guarantee how it will work cross-browser and cross-platform.
You have a point. I hadn't thought of that.
Robert,
I tried running you demo (index.html) with IE7 and it gives me an error on line 147 – Access is denied.
"this.xmlHttp.onreadystatechange = this.getResults;"
It works as expected in Firefox.
Anything I have missed?
KK AW,
To make sure AJAX calls work in IE, it has to be run on a server (IIS, Apache etc). Just opening files locally and trying to make AJAX requests in IE will just give you an error.
Thanks. I will try that.
Happy New Year 2008.
Hi… i like this wonderful AJAX SUGESSTIONS script. Works fine and looks quite nice.
BUT: in the Moment i try to load prototype.js on the same page, i will get errors in IE and Suggestions does not work any longer.
Error message e.g. "'this.elements.length' is null or no object". Any ideas how to work with it?
Daniel
Daniel,
I've never used Prototype, but my guess is that they implement something which affect elements, thus it might result in unexpected behavior of some parts.
Would like to ask, is it me with php on my win box, on my IE6,
that after i select an item it returns values in URL form, that i need to unescape it?
Or everyone's getting values in the same format?
Real cool script! Thank you very much!!!
Any news on the IE7 bug as I cant get it not to add the full url of the script I am running,
Plus why in other browsers does it add ; to the end of the string within the element once you have selected an item from the list items returned?
jon,
I’m sorry, but I’m doing no further work on this script. Feel free to alter to it your needs!
Aldo,
Sorry, nothing has changed with the script. As my suggestion above, you need to change the code yourself if you need to alter it.
Hello, I'll may sound like a fool asking this… but I feel like I need a little more instructions on how to setup this in my web server, these are my basic question:
– is already fixed the IE7 (full url in field) issue?
– if not, could you please give me a suggestion on how to tweak this code to add just the searched term to the input field?
thanks in advance!!
Fixing IE7 Href Bug
1) Inside Href tag, put a separator like '|||" before the text you want to be in input text field after selection has been made.
i.e: a href="|||1" class="item choose-value"
2) Open ajaxSuggestions.js. Search for "href" and replace the line and the next two with the following code:
var value = elm.getAttribute("href").split(escape("|||"));
if(!new RegExp(value).test(input.value)){
input.value = ((input.value.length > 0 && /;/i.test(input.value))? (input.value + value[1]) : value[1]) + ajaxSuggestions.itemSeparator;
}
That's it
Thanks Rodrigo for the fix!!
Roberto and all the other guys:
Is there a way to have the ajax-suggestions script to work in multiple domains?
The tutorial shows to do it this way:
– It works great in the same domain, but what my website have many sub domains, like search.mypage.com, store.mypage.com, docs.mypage.com … in my particular case the only php enabled host is the search.mydomain.com so I cant call locally the search-uni.php from the other domain pages, so I tried this:
And it didn't work… what do you recommend me to do?
Thanks in advance guys
Aldo
tutorial way: input type="text" class="ajax-suggestion url-search-uni.php"
my test: input type="text" class="ajax-suggestion url -http://search.crafta.com/search-uni.php" (did not worked)
Rodrigo,
Haven't tested it, but if it works: great!
Aldo,
As the source is now, it doesn't support some of those characters. This line needs to be tweaked to make it happen:
<code>var url = this.currentElm.className.replace(/.*url-([w/?.-]+).*/, "$1");</code>.
Should be pretty easy to just add : and perhaps something more, but I have a ton of other things to do at the moment. Sorry.
Thanks Robert, as I dont know nothing about regex, can please somebody tellme how to tweak this line in order to accept the "http://www.mydomain.com/" suffix.
var url = this.currentElm.className.replace(/.*url-([w/?.-]+).*/, "$1");
Thanks in advance!
=)
Hello, can please somebody give me a hand with this?
Any help will be appreciated!
=)
Some body told me that should to the trick, and it does, it calls the external file… BUT when trying to tes it … I get a "NS_ERROR_DOM_BAD_URI" … I think because the script cant httprequest an external file…
[xmp]
var url = this.currentElm.className.replace(/.*url-([^s"]+).*/, "$1");
[/xmp]
Is there another think that I can try to have it work for cross-domain?
thanks agian =)
And how to set character encoding? I'm using UTF-8, and some characters just ends being converted to | with this AJAX Suggestions π
Tried to put meta with character encoding on the url- site, but that hasn't changed anything π
zOMG, turns out it was my PHP function fault, not your JS – sorry.
Sorry for triple posting, but I’ve ran into another problem. When entering more than one input item, it would reset my input. I’ve tracked down the problem and it turned out to be because I’ve changed the itemSeparator. I’ve googled some Javascript tutorials, and came up with this replacement:
Replace [around line 295]:
input.value = ((input.value.length > 0 && /;/i.test(input.value))? (input.value + value) : value) + ajaxSuggestions.itemSeparator;
With:
if( (input.value.length > 0 && new RegExp(ajaxSuggestions.itemSeparator, "i").test(input.value) ) )
{
var brokenstring=input.value.split(ajaxSuggestions.itemSeparator);
input.value = '';
for (var i = 0; i < (brokenstring.length-1); i++)
{
input.value += brokenstring[i] + ajaxSuggestions.itemSeparator;
}
input.value = input.value + value + ajaxSuggestions.itemSeparator;
}
It seems to be working like a charm, and also takes care of the string you’ve entered and that was searched for (so no more “sugSuggestion” or something like that).
Also, if someone wants to implement this in PHP script and want the suggestion to work only for the last input (separated by itemSeparator, that is), you can do it like me:
$input = trim($_GET['search']);
$input = explode(',', $input); //insert your itemSeparator here!
rsort($input);
$input = array_shift($input);
$input = trim($input);
Hope that helps someone.
It actually should be:
<code>$input = array_reverse($input);</code>
instead of:
<code>rsort($input);</code>
in my PHP part. Sorry, my bad.
Mori,
Glad it worked out! Thanks for the tip!
Hi
I am currently using your ajax suggestions script on my website, its working amazingly however at the bottom of the search results I would really like to have a link “Full Results” that would allow the user to get more results on a main search page. The problem is that I cant work out how to update this link with the search term at the end. I noticed you currently have this implemented in the search box on your site so i know its possible, but i just cant figure it out π
For example the “Full Results” link would update from
search.php?q=
to
search.php?q=searchKeyword
Thanks for reading π
Ross
Ross,
In your page receiving the AJAX call, you already use the term to produce the search results, right? Meaning, you can customize any link to Full results in any way you want it in that page too, just add it below the separate search results you already return.
Hi Robert
Thanks for your quick response π
I was missing the obvious there!
Its working fine now, thanks again
Ross
Ross,
Great to hear! π
So I’m presenting a list of results like so:
<ul>
<li><a href="http://example.com">Example.com</a></li>
<li><a href="javascript: myFunc(somevar);">JS Link</a></li>
</ul>
In Firefox the regular <a>’s work just fine, the javascript ones do nothing, even with a simple javascript: alert(“Hello World”); as the href attribute. Is there a workaround for this?
In IE7 everything works as expected. This has me flummoxed, as I’m supposed to be able to blame my difficulties on IE; I feel guilty for being upset with FF.
Any ideas?
Correction:
Nothing works in FF. In IE, I get the following behavior:
javascript: alert("Hello World"); works just fine.
javascript: myFunc(someVar); works if I tab to the result and press enter, but does nothing if I mouse over and click the link.
Trevor,
I understand the sentiment about Firefox… π
What if you were to try events like <code>onclick="doSomething()"</code> instead of the <code>href</code> attribute?
Trevor,
I had the same problem. i used onfocus as well as onkeyup which fixed the issue. this does however make an ajax call everytime you click the input field, but you can easily program it to check if the value has changed before it uses the onfocus call.
Also, I am actually having a problem myself. Seems that you cannot type an ampersand in the search field in say Google Chrome / Safari, but you can in Firefox. Only happens once the suggest box has loaded. Can't seem to find any reference to it as a keycode, so i'm not sure what is going on. Maybe a browser specific glitch?
hello,
I think it's a great script and very easy to configure.
I have one problem though – it's related to the position of the suggestion box against the input box. It's not always in the same position (vertically) but "moves" right, depending what size the browser window is set to. I.e. 800×600 – it's aligned as expected. 1024×1768 – slightly right, lets say 10 pixels margin compared to the input field. 1280×1024 – suggestion box starts in the middle if the input field.
Any idea why this is happening? Offset parameters are set 0.
Regards,
Adam
Martin,
Hmm, sounds weird with the ampersands. I don't know what that can be.
Adam,
No idea, actually. I briefly looked over the script, and I can't see why that would happen. Sorry.
Hi all,
Its actually related to an old post, does the classname parameter supports url in form http://some url:port/ etc. if not can anybody tell the resolution
basically its how to replace this to accept the url in above format
var url = this.currentElm.className.replace(/.*url-([w/?.-]+).*/, "$1");
Couple of important notes… heh.
Someone mentioned unescape earlier… i noticed this happening in IE6 for Win. so if my result was "Las Vegas" in my suggest popup, when clicked, it would show as "Las%20Vegas" in the input.
after this line:
input.value = ((input.value.length > 0 && /;/i.test(input.value))? (input.value + value[value.length-1]) : value[value.length-1]) + ajaxSuggestions.itemSeparator;
i added this:
input.value = unescape(input.value);
so far working well.
Also, i used this script in conjunction with a dropdown box which changed what it should suggest. I used JS to change the class to a different URL for a different search / suggestions upon changing the dropdown. You have to re-initialize the suggestion script when you change the class on the fly.
ie.
document.getElementById("my_search").className = 'ajax-suggestion url-my_suggest.php';
ajaxSuggestions.init();
Cheers.
I can't apply ajax suggestion in codeigniter. Can you show me. I think it concern to link url
@Mori, Your code works great when you enter a second (third, fourth and so on) value in the input field, but is not working on the first one as the original code did… this is happening in Firefox and Safari for Mac :-(.
Hi, your script is great, however I have some problem. I want to categorise the suggestions. I mean… There's a search-type and a search-value field on my site. If I set the search-type e.g. to a city, I don't want to list countries.
I've tried to modify the url in the class (of the input) by JS, to give the PHP file an extra paameter depending on the search-type, but it's not working. The class can be changed, but the suggestion listing can't be affected.
I've also tried to modify your JS to see what I can do, but I quicly realised that I'm not good enough. (I added the parameter to the url, but it caused the suggestion not work.)
I'll be glad if anyone could tell me how can I categrise, or if this function would be included in the code.
Thanks in advance!
Hi. First of all: congratulations. You´re officialy a genious. Or at least you´re a hero.
I have one problem: I can´t send variables like: url-file.php?value=1&id=5
I tried adding "&" and the same in the script but nothing happens.
thanks in advance!
Thanks everyone for using it!
Unfortunately I don't have the time to support it anymore, although you are of course free to post any issues here in case someone else has a good reply.