Simple auto-complete function
For everybody who wants a way to create an auto-complete or auto-suggest text box in JavaScript, without use of prototype handlers and other advanced things, here is a sample that works perfectly.
It gives you an auto-complete like Outlook Express does an auto-complete when you type e-mail address that belongs to Address Book...
Let's create an sample of a text box that completes e-mail addresses, when you type them.
First of all, you need to create an array with all values used to auto-complete, in this case, three e-mail addresses, but you can add how many addresses (elements) you need.
After add all elements, use sort() method to order your array.
Code:
var aMail = new Array("albert@mail.com","beth@mail.com","harry@mail.com");
aMail.sort();
Now you need to use a standard handler (onKeyUp) of textbox to call the auto-complete function:
Code:
<input type="text" name="anyName" onKeyUp="Complete(this, event)">
Note we call the function "Complete" (showed below) with two arguments. The first is the keyword "this" to pass the textbox object to function, so we don't need to manipulate names or IDs of textboxes and we can work with multiple textboxes on a page). The second parameter is the keyword "event" to pass the properties of onKeyUp event, so we can handle which key was pressed on the keyboard.
Finally we have the function. As you guess, the name of the function is Complete and here is its code:
Code:
function Complete(obj, evt) {
if ((!obj) || (!evt) || (aMail.length == 0)) {
return;
}
if (obj.value.length == 0) {
return;
}
var elm = (obj.setSelectionRange) ? evt.which : evt.keyCode;
if ((elm < 32) || (elm >= 33 && elm <= 46) || (elm >= 112 && elm <= 123)) {
return;
}
var txt = obj.value.replace(/;/gi, ",");
elm = txt.split(",");
txt = elm.pop();
txt = txt.replace(/^\s*/, "");
if (txt.length == 0) {
return;
}
if (obj.createTextRange) {
var rng = document.selection.createRange();
if (rng.parentElement() == obj) {
elm = rng.text;
var ini = obj.value.lastIndexOf(elm);
}
} else if (obj.setSelectionRange) {
var ini = obj.selectionStart;
}
for (var i = 0; i < aMail.length; i++) {
elm = aMail[i].toString();
if (elm.toLowerCase().indexOf(txt.toLowerCase()) == 0) {
obj.value += elm.substring(txt.length, elm.length);
break;
}
}
if (obj.createTextRange) {
rng = obj.createTextRange();
rng.moveStart("character", ini);
rng.moveEnd("character", obj.value.length);
rng.select();
} else if (obj.setSelectionRange) {
obj.setSelectionRange(ini, obj.value.length);
}
}
Done! Everything is finished.
When you type a text that that begins with letter "a", the rest of mail address "lbert@mail.com" will be completed in textbox.
So let's look the function.
The first thing we do is ensure that function have all necessary objects to work.
The "obj" variable receives the "this" parameter = textbox object.
The "evt" variable receives the "event" parameter = onKeyUp event properties.
The "aMail" variable is the array created with elements to auto-suggest.
If one of these variables is missing we quit the function.
Code:
if ((!obj) || (!evt) || (aMail.length == 0)) {
return;
}
Now, we need to check if textbox have something typed. If not there's nothing to complete, so quit function
Code:
if (obj.value.length == 0) {
return;
}
Now we need to know which key was typed in textbox. This can be done reading the properties of event. In the case of MSIE this property is named "KeyCode" and in the case of Mozilla this property is named "which".
With the code below, we set the "elm" variable with the right value of the key in accordance with the browser.
Code:
var elm = (obj.setSelectionRange) ? evt.which : evt.keyCode;
We need to evaluate the key code aborting the function in the case of non-text keys pressed (arrows, delete, backspace, etc.).
Code:
if ((elm < 32) || (elm >= 33 && elm <= 46) || (elm >= 112 && elm <= 123)) {
return;
}
By now, we separate all text wrote at textbox, to get the last address of e-mail typed.
In this case we can separate e-mail addresses by coma (,) or semicolons (;).
Note this code is optional, since we are creating an textbox for mail addresses. This code is to ensure that we always are working with te last address typed in textbox.
Code:
// change all semicolons (;) by comas (,)
var txt = obj.value.replace(/;/gi, ",");
// create an array spliting text by comas
elm = txt.split(",");
// get the last element of array
txt = elm.pop();
// remove any spaces of the beginning of the text
txt = txt.replace(/^\s*/, "");
// ensure the text is greater of zero after removing spaces
if (txt.length == 0) {
return;
}
And now, we have to retrive the initial position of the selection in textbox
This can be done by different ways in MSIE and Mozilla, so we have to detect browser and get the information in accordance with it.
Code:
if (obj.createTextRange) {
// MSIE
var rng = document.selection.createRange();
if (rng.parentElement() == obj) {
elm = rng.text;
var ini = obj.value.lastIndexOf(elm);
}
} else if (obj.setSelectionRange) {
// Mozilla
var ini = obj.selectionStart;
}
After this, we search the array using a for loop, searching the text typed in textbox. In the case of match, we break the loop.
If a text is found it is appended to the end of text in textbox.
Code:
for (var i = 0; i < aMail.length; i++) {
elm = aMail[i].toString();
if (elm.toLowerCase().indexOf(txt.toLowerCase()) == 0) {
obj.value += elm.substring(txt.length, elm.length);
break;
}
}
Now, we add a selection to the text appended. This is done by different ways in the case of Mozilla or MSIE:
Code:
if (obj.createTextRange) {
rng = obj.createTextRange();
rng.moveStart("character", ini);
rng.moveEnd("character", obj.value.length);
rng.select();
} else if (obj.setSelectionRange) {
obj.setSelectionRange(ini, obj.value.length);
}
That's all.
Below is a complete sample of a page with this function and a textbox.
Code:
<html>
<head>
<title>Test</title>
<script language="javascript" type="text/javascript">
<!--
var aMail = new Array("albert@mail.com","beth@mail.com","harry@mail.com");
aMail.sort();
function Complete(obj, evt) {
if ((!obj) || (!evt) || (aMail.length == 0)) {
return;
}
if (obj.value.length == 0) {
return;
}
var elm = (obj.setSelectionRange) ? evt.which : evt.keyCode;
if ((elm < 32) || (elm >= 33 && elm <= 46) || (elm >= 112 && elm <= 123)) {
return;
}
var txt = obj.value.replace(/;/gi, ",");
elm = txt.split(",");
txt = elm.pop();
txt = txt.replace(/^\s*/, "");
if (txt.length == 0) {
return;
}
if (obj.createTextRange) {
var rng = document.selection.createRange();
if (rng.parentElement() == obj) {
elm = rng.text;
var ini = obj.value.lastIndexOf(elm);
}
} else if (obj.setSelectionRange) {
var ini = obj.selectionStart;
}
for (var i = 0; i < aMail.length; i++) {
elm = aMail[i].toString();
if (elm.toLowerCase().indexOf(txt.toLowerCase()) == 0) {
obj.value += elm.substring(txt.length, elm.length);
break;
}
}
if (obj.createTextRange) {
rng = obj.createTextRange();
rng.moveStart("character", ini);
rng.moveEnd("character", obj.value.length);
rng.select();
} else if (obj.setSelectionRange) {
obj.setSelectionRange(ini, obj.value.length);
}
}
// -->
</script>
</head>
<body>
<form name="anyForm">
<input type="text" name="anyName" onKeyUp="Complete(this, event)">
</form>
</body>
</html>
Ok? It's simple!!! Like JavaScript is... ;)
Simple auto-complete function
BuezaWebDev, thanks for your time in puttting together this code. I didn't expect anyone to just bust something out. Too cool man! But i'm not exactly sure how to incorporate it into my existing app and your USE CASE is slightly off (sorry). To follow up on USE CASE here it is:
1. The user types in "smi" all the smiths appear.
2. User chooses John Smith and it sets the text field to "John Smith" (not to john smiths email address).
3. User hits Tab Key and activates onBlur event
4. Event triggers function to confirm that "John Smith" is in the "aName" array.
** So heres my question, how can I use JUST your validation function (you so gladly put together) in my existing .js file and just have it validate that the name they typed into the form, matches a name from the array.
BELOW is the code I am working from.
Code:
var aName = new Array(
"John Smith",
"Harry Whodini",
"Dill Doe",
"Robyn Hood",
"Andy Anderson",
"Jimmy Johnson",
"Chris Angel",
"Kevin Mitnick"
);
aName.sort();
function Complete(obj, evt) {
if ((!obj) || (!evt) || (aName.length == 0)) {
return;
}
if (obj.value.length == 0) {
return;
}
var elm = (obj.setSelectionRange) ? evt.which : evt.keyCode;
if ((elm < 32) || (elm >= 33 && elm <= 46) || (elm >= 112 && elm <= 123)) {
return;
}
var txt = obj.value.replace(/;/gi, ",");
elm = txt.split(",");
txt = elm.pop();
txt = txt.replace(/^\s*/, "");
if (txt.length == 0) {
return;
}
if (obj.createTextRange) {
var rng = document.selection.createRange();
if (rng.parentElement() == obj) {
elm = rng.text;
var ini = obj.value.lastIndexOf(elm);
}
} else if (obj.setSelectionRange) {
var ini = obj.selectionStart;
}
for (var i = 0; i < aName.length; i++) {
elm = aName[i].toString();
if (elm.toLowerCase().indexOf(txt.toLowerCase()) == 0) {
obj.value += elm.substring(txt.length, elm.length);
break;
}
}
if (obj.createTextRange) {
rng = obj.createTextRange();
rng.moveStart("character", ini);
rng.moveEnd("character", obj.value.length);
rng.select();
} else if (obj.setSelectionRange) {
obj.setSelectionRange(ini, obj.value.length);
}
}
I want to add a function to the end of this code to validate that the name they typed into the form, matches a name form the array. If there is a match they move to the next formField. If no match is found it alerts them and reset's focus back to the form field.
Thanks so much for your help. Were so close...