TextboxList meets Autocompletion

In my previous blogpost I explained how to extend TextboxList to add closing functionality via a link added to each box. But it was missing an important ingredient: autocompletion!
Again, all we have to do is extend the TextboxList class, override some methods, some events, and create some new ones (all prefixed by auto)
Click here to see Javascript code
var FacebookList = new Class({Extends: TextboxList,data: [],options: {onInputFocus: function() { this.autoShow(); },onInputBlur: function(el) {el.value = '';this.autoHide();},onBoxDispose: function(item) {this.autoFeed(item.$attributes.$text);},autocomplete: {'opacity': 0.8,'maxresults': 10,'minchars': 1}},initialize: function(element, autoholder, options) {arguments.callee.parent(element, options);this.autoholder = $(autoholder).set('opacity', this.options.autocomplete.opacity);this.autoresults = this.autoholder.getElement('ul');var children = this.autoresults.getElements('li');children.each(function(el) { this.add(el.innerHTML); }, this);},autoShow: function(search) {this.autoholder.setStyle('display', 'block');this.autoholder.getElements('*').setStyle('display', 'none');if(! search || ! search.trim() || (! search.length || search.length < this.options.autocomplete.minchars )){this.autoholder.getElement('.default').setStyle('display', 'block');this.resultsshown = false;} else {this.resultsshown = true;this.autoresults.setStyle('display', 'block').empty();this.data.filter(function(str) { return str ? str.test(search, 'i') : false; }).each(function(result, ti) {if(ti >= this.options.autocomplete.maxresults) return;var el = new Element('li').set('html', this.autoHighlight(result, search)).inject(this.autoresults);el.$attributes.$result = result;if(ti == 0) this.autoFocus(el);}, this);}},autoHighlight: function(html, highlight) {return html.replace(new RegExp(highlight, 'gi'), function(match) {return '<em>' + match + '</em>';});},autoHide: function() {this.resultsshown = false;this.autoholder.setStyle('display', 'none');},autoFocus: function(el) {if(! el) return;if(this.autocurrent) this.autocurrent.removeClass('auto-focus');this.autocurrent = el.addClass('auto-focus');},autoMove: function(direction) {if(!this.resultsshown) return;this.autoFocus(this.autocurrent['get' + (direction == 'up' ? 'Previous' : 'Next')]());},autoFeed: function(text) {if(this.data.indexOf(text) == -1)this.data.push(text);},autoAdd: function(el) {if(!el || ! el.$attributes.$result) return;this.add(el.$attributes.$result);delete this.data[this.data.indexOf(el.$attributes.$result)];this.autoHide();this.current.$attributes.$input.value = '';},createInput: function(options) {var li = arguments.callee.parent(options);var input = li.$attributes.$input;input.addEvents({'keydown': function(e) {e = new Event(e);this.dosearch = false;switch(e.code) {case Event.Keys.up: return this.autoMove('up');case Event.Keys.down: return this.autoMove('down');case Event.Keys.enter:this.autoAdd(this.autocurrent);this.autocurrent = false;this.autoenter = true;break;default: this.dosearch = true;}}.bind(this),'keyup': function() {if(this.dosearch) this.autoShow(input.value);}.bind(this)});input.addEvent(Browser.Engine.trident ? 'keydown' : 'keypress', function(e) {if(this.autoenter) new Event(e).stop();this.autoenter = false;}.bind(this));return li;},createBox: function(text, options) {var li = arguments.callee.parent(text, options);li.addEvents({'mouseenter': function() { this.addClass('bit-hover') },'mouseleave': function() { this.removeClass('bit-hover') }});li.adopt(new Element('a', {'href': '#','class': 'closebutton','events': {'click': function(e) {new Event(e).stop();if(! this.current) this.focus(this.maininput);this.dispose(li);}.bind(this)}}));li.$attributes.$text = text;return li;}});window.addEvent('domready', function() {// initvar tlist2 = new FacebookList('facebook-demo', 'facebook-auto');// fetch and feednew Request.JSON({'url': 'json.html', 'onComplete': function(j) {j.each(tlist2.autoFeed, tlist2);}}).send();});
It works by caching all the results from a JSON Request and feeding them to the autocompleter object. When a item is added as a box, it’ removed from the feed array, and when the box is disposed it’s added back, so that it becomes available in the list when the user types.
Another new feature is that you’ll be able to let it add boxes from the HTML directly:
<label>FacebookList input</label><input type="text" value="" id="facebook-demo" /><div id="facebook-auto"><div class="default">Type the name of an argentine writer you like</div><ul class="feed"><li>Jorge Luis Borges</li><li>Julio Cortazar</li></ul></div>
The constructor now takes new parameters to configure the autocompletion, like the minimum number of characters to trigger the dropdown, and more.
Changelog
- 0.1: initial release
- 0.2: added click support, removed $attributes use, code cleanup
Download
Click here to download the zip with code and examples
Tags: on January 12th, 2008
April 12th, 2008 at 3:08 pm
nice and usefull
April 12th, 2008 at 2:57 pm
loco, no se cuanto sabes de ajax… pero me parece que de literatura argentina sabes bastante
April 3rd, 2008 at 10:31 am
Here is an example code to put in the domready :
window.addEvent(’domready’, function() {
var tlist2 = new FacebookList(’facebook-demo’, ‘facebook-auto’);
// fetch and feed
new Request.JSON({’url’: ‘json.html’, ‘onComplete’: function(j) {
j.each(tlist2.autoFeed, tlist2);
}}).send();
$(’facebook-form’).addEvent(’submit’, function(){ //facebook-form is the form id
tlist2.update();
})
});
One problem remaining on IE :
pressing enter while the autocomplete list opened submit the form instead of just adding the list item like the Fx / Opera behavior
March 31st, 2008 at 2:36 pm
I’m with JP. I’m not sure where to put the update function. So if someone could explain it that would great!
Thanks.
March 30th, 2008 at 6:41 pm
after several hours of fustration i figured it out ! (Using ninja’s method) but it was even easier. I left the update function as is!
// Like ninja said. Placing tlist2 outside the addEvent.
var tlist2 = BandsList;
window.addEvent(’domready’, function() {
tlist2 = new BandsList(’bands’, ‘bands-auto’);
// fetch and feed
new Request.JSON({’url’: ‘json.php’, ‘onComplete’: function(j) {
j.each(tlist2.autoFeed, tlist2);
}}).send();
});
//// === in the HTML form ==== ///
<input type=”submit” onClick=”javascript:tlist2.update();” value=”Submit” />
March 30th, 2008 at 6:40 pm
after several hours of fustration i figured it out ! (Using ninja’s method) but it was even easier. I left the update function as is!
// Like ninja said. Placing tlist2 outside the addEvent.
var tlist2 = BandsList;
window.addEvent(’domready’, function() {
tlist2 = new BandsList(’bands’, ‘bands-auto’);
// fetch and feed
new Request.JSON({’url’: ‘json.php’, ‘onComplete’: function(j) {
j.each(tlist2.autoFeed, tlist2);
}}).send();
});
//// === in the HTML form ==== ///
March 30th, 2008 at 5:26 pm
Hello everyone!
Where do i put the update() function!? People say they figured it out but don’t post the solution!
March 30th, 2008 at 12:20 pm
Oppps..ditch that asp.net part in my comment, it was a stupid copy paste error….
Thanks for sharing, this is super cool
March 30th, 2008 at 12:16 pm
mega slick…loong live ASP.NET
March 29th, 2008 at 9:58 am
Hi,
Could anybody post a working example of how to get the data posted? What needs to be changed, and where?
Many thanks
March 26th, 2008 at 2:33 pm
Hi. This is wonderful stuff. I’ve used this in one of my projects and has proved a great success. But like everything, it has some issues. Here’s what it is..
I’m updating the dropdown list, i.e. fetchfile on every keyword selection. e.g. If i search abc and get 10 results. I select one of them. Then i search again (type again). Now i’m making a new JSONString which returns say 2 results. But my drop down is showing 9 keywords options.
In short, the issue is i want to update the drop down list on every hit. The JSONString i’m returning is updated but the drop down list is not..
can the authors or anyone else please help me out on this.. Kinda urgent matter.
Many thanks.
March 24th, 2008 at 7:17 am
i made my own list for autocomplete, but i want that box be empty at first - how can i ?
March 23rd, 2008 at 11:52 pm
[...] YUI Autocomplete AJAX Select Drowdown with ID AutoCompleter Tutorial - jQuery(Ajax)/PHP/MySQL Yahoo! AutoComplete jQuery Autocomplete Mod FaceBoox style autosuggest with jQuery Implementing AJAX Suggest and Autocomplete Ajax autosuggest/autocomplete from database Ajax Auto Suggest v.2 Facebook List TextboxList with AutoCompletion Proto!MultiSelect 0.2 TextboxList meets Autocompletion [...]
March 19th, 2008 at 7:16 pm
[...] Facebook TextboxList with Autocompletion. that’s a pretty good trick of having the input box inside a fake input box. [...]
March 12th, 2008 at 7:24 pm
is there anyway to only allow only one name to be submitted, instead of an unlimited amount?