Input control with type-ahead
Making an input field with type-ahead was quite a challenge, but very useful when you want to allow users to enter their own values, but suggest already existing. First I decided to use some clever HTML: the className 'tah' of the span will trigger the JavaScript; the div to show the items found is already present and the span in the span contains the options that already exist. For bigger lists, you would have to retrieve parts of it with Ajax.
The HTML
<span class="tah">
<input id="fDocTags" />
<div></div>
<span>one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,
thirteen,fourteen,fifteen,sixteen</span>
</span>
The CSS
Then I added a little CSS to model the tags:
/* typeahead */
.tah{position:relative}
.tah span{display:none}
.tah div{position:absolute;top:22px;left:0;width:200px;border:solid 1px #ccc;padding:2px;display:none}
.tah div b{display:block;width:100%;cursor:pointer}
'G', my JavaScript global namespace
It has become a habit of mine: collecting all the generic JavaScript functions I need in a global namespace 'G', accompagned by the ever-present $ and $$ shortcuts:
var G={
init:function(){var o=G,n;for(n in o)if(o[n].init)o[n].init()},
set:function(a,b){for(var o in b)a[o]=b[o];return a},
mid:function(a,b,c){var o=a.split(b);return(o.length<2)?'':o[1].split(c)[0]},
last:function(a,b){var o=a.split(b);return o[o.length-1]},
create:function(a,b,c,d){var o=(d||document).createElement(a);G.set(o,b);G.set(o.style,c);return o},
append:function(a,b,c,d){var o=b.tagName?b:G.create(b,c,d);a.appendChild(o);return o}
},
$=function(a){return document.getElementById(a)},
$$=function(a,b){return (b||document).getElementsByTagName(a)}
window.onload=G.init
The 'auto' JavaScript object
I know 'auto' is a bit weird, but I think it stood for Auto Complete at first. Instead of trapping keyboard strokes, I use an observer to check the value of the input field from time to time once this field gets focus. Pay special attention to the 'setCursor' function: it moves the cursor to the end of the field.
G.auto={
init:function(){
var m=this,o=$$('span'),i
for(i=0;i<o.length;i++){if(o[i].className=='tah')m.prepare(o[i])}
},
prepare:function(a){var m=this,o=$$('input',a)[0];o.onfocus=m.focus;o.onblur=m.blur},
focus:function(){
var m=G.auto,o=this,op=this.parentNode
m.target=o
m.items=','+$$('span',op)[0].innerHTML+','
m.div=$$('div',op)[0]
m.observer=setInterval(m.observe,500)
},
blur:function(){var m=G.auto;clearInterval(m.observer);setTimeout(m.done,100)},
done:function(){G.auto.div.style.display=''},
observe:function(){
var m=G.auto,v=G.last(m.target.value,','),o,i,q=[],t=m.div
if(v!='')o=m.items.match(new RegExp(','+v+'[^,]*','gi'))
if(!o){t.style.display='';return}
t.innerHTML=''
for(i=0;i<o.length;i++)G.append(t,'b',{innerHTML:o[i].substr(1),onclick:m.click})
m.div.style.display='block'
},
click:function(){
var m=G.auto,o=m.target,v=o.value.split(','),n
v[v.length-1]=this.innerHTML
o.value=v.join(',')
n=o.value.length
m.setCursor(o,n,n)
},
setCursor:function(a,b,c){
var r,q='character'
if(a.setSelectionRange){
a.setSelectionRange(b,c)
a.focus()
}
else if(a.createTextRange){
r=a.createTextRange()
r.collapse(true)
r.moveEnd(q,c)
r.moveStart(q,b)
r.select()
}
}
}
The result
Here's the sample page. You can test by typing number names like 'seven', 'twelve' separated by commas.
Comments
21.11.2007 00:23:54, Jan Schulz
Tried it (using opera 9.5beta): tried 'sev' and 'down' -> does not work, I have to use the mouse :-( -> would be great if you could listen for down and up and select the right one. Also: I can get one tag, but not two, the comma does not put the box into 'suggeste-mode' again :-(
Anyway: nice one :-)
19/02/2008 14:52:27, Erwin Heeren
When I try it, the Type Ahead list is displayed, but I cannot select a value from that list :-( Is it just me?
19/02/2008 19:19:10, Michel Van der Meiren
You have to click on one with your mouse. That doesn't work?
20/02/2008 10:18:02, Erwin Heeren
Works now (sometimes) ... You must click at the exact location and not anywhere in the list.
To add a comment, log in or register as new user. It's free and safe.