Main Page Content
Dynamically Filtering Dropdown Lists In Javascript
This article describes a technique that takes input from a form text field and uses it to bring matching options to the top in a dropdown listHave you ever hit a dropdown box that was just too long?There were so many items that finding the one you wanted was a hassle.You hit the first letter of the option you were looking for, only to find that there are at least 50 options starting with that letter - or worse, the list wasn't sorted alphabetically and you had to type the letter 20 times to get to your option.Maybe the option you wanted was phrased differently than you expected. It didn't even start with the letter you were typing!Sound familliar? Well, there are a couple of solutions.One way around this problem is to stage your dropdown boxes;what you choose in box 1 determines your options in box 2, etc.Joe's article,Creating Dynamic Select Boxes, describes how this can be done using ColdFusion, JavaScript and WDDX. (if there is sufficient interest, I'll throw together an article that presents a Javascript-only method)This is often a great way to reduce cumbersome lists into managable chunks.However this technique is based on the assumption that your visitors understand the way you've categorised the options.Take for example a simple dropdown list of countries. In the past, I've found my beloved Australia under categories such as "Asia", "Pacific", "South Pacific", and, "Oceania". Searching for options this way can be a bit hit and miss.Another solution to the "long list" problem is to allow your visitor to make their option magically rise to the top of the list...
If the visitor does several searches, the dropdown list could get rather ugly from the constant shuffling. To combat this, we make a copy of the dropdown list and use that as the master copy. We don't want to do this every time the list is filtered - just the first time.The first thing the function does is check whether a master copy exists. If one does not, then it attaches and populates a master copy array to the form's select objectThe function then creates two arrays: one for matches and one for non-matches. Now, as it iterates through the master copy, it appends items to either of the two arrays.Once each item in the dropdown has been assessed, the matches and non-matches arrays are written back to the select object.Finally, the function forces the first item in the list to be selected - this makes sure that the matching options are immediately apparent to the visitorAllowing Advanced Search
If you're not fussed about supporting version three browsers, you can enhance the function by having it match by regular expression.Simply add this near the top of the function:
... with...
Your visitors will now be able to type:
The following table lists the browsers and platforms on which I have tested this code.
Search:
The Code
For those who are in a hurry (or who are already sick of reading), here is the code with ample comments:<html><head> <title>Dropdown Filter</title></head><body>
<script language="JavaScript" type="text/javascript"><!--/* - Give Credit Where Its Due - Please acknowledge this article and its author, at least in code comments, when using this code.Author: Justin Whitford
Source: www.evolt.orgThank you.
*//*
filtery(pattern, list) pattern: a string of zero or more characters by which to filter the list list: reference to a form object of type, selectExample:
<form name="yourForm"> <input type="text" name="yourTextField" onchange="filtery(this.value,this.form.yourSelect)"> <select name="yourSelect"> <option></option> <option value="Australia">Australia</option> .......*/function filtery(pattern, list){ /* if the dropdown list passed in hasn't already been backed up, we'll do that now */ if (!list.bak){ /* We're going to attach an array to the select object where we'll keep a backup of the original dropdown list */ list.bak = new Array(); for (n=0;n<list.length;n++){ list.bak[list.bak.length] = new Array(list[n].value, list[n].text); } }/*
We're going to iterate through the backed up dropdown list. If an item matches, it is added to the list of matches. If not, then it is added to the list of non matches. */ match = new Array(); nomatch = new Array(); for (n=0;n<list.bak.length;n++){ if(list.bak[n][1].toLowerCase().indexOf(pattern.toLowerCase())!=-1){ match[match.length] = new Array(list.bak[n][0], list.bak[n][1]); }else{ nomatch[nomatch.length] = new Array(list.bak[n][0], list.bak[n][1]); } }/*
Now we completely rewrite the dropdown list. First we write in the matches, then we write in the non matches */ for (n=0;n<match.length;n++){ list[n].value = match[n][0]; list[n].text = match[n][1]; } for (n=0;n<nomatch.length;n++){ list[n+match.length].value = nomatch[n][0]; list[n+match.length].text = nomatch[n][1]; }/*
Finally, we make the 1st item selected - this makes sure that the matching options are immediately apparent */ list.selectedIndex=0;}// --></script><form name="yourForm">
Search <input type="text" name="yourTextField" onkeyup="filtery(this.value,this.form.yourSelect)" onchange="filtery(this.value,this.form.yourSelect)"> <select name="yourSelect"> <option></option> <option value="Australia">Australia</option> <option value="China">China</option> <option value="England">England</option> <option value="New Zealand">New Zealand</option> </select></form></body></html>
Discussion
How it WorksIf the visitor does several searches, the dropdown list could get rather ugly from the constant shuffling. To combat this, we make a copy of the dropdown list and use that as the master copy. We don't want to do this every time the list is filtered - just the first time.The first thing the function does is check whether a master copy exists. If one does not, then it attaches and populates a master copy array to the form's select objectThe function then creates two arrays: one for matches and one for non-matches. Now, as it iterates through the master copy, it appends items to either of the two arrays.Once each item in the dropdown has been assessed, the matches and non-matches arrays are written back to the select object.Finally, the function forces the first item in the list to be selected - this makes sure that the matching options are immediately apparent to the visitorAllowing Advanced Search
If you're not fussed about supporting version three browsers, you can enhance the function by having it match by regular expression.Simply add this near the top of the function:
pattern = new RegExp(pattern,"i");
... and replace... if(list.bak[n][1].toLowerCase().indexOf(pattern.toLowerCase())!=-1){
... with...
if(pattern.test(list.bak[n][1])){
Your visitors will now be able to type:
- "^au" to get all items starting with au.
- "ia$" to get all items ending with ia.
- "9\d\d" to get all items containing numbers between 900 and 999.
- etc, etc.
The following table lists the browsers and platforms on which I have tested this code.
Browser Compatability Table | |||
---|---|---|---|
Browser / Ver | Win NT | Win 98 | Linux (SuSE 8) |
Internet Explorer 5.5 | Y | - | - |
Internet Explorer 6.0 | - | Y | - |
Netscape 3.x | Y | - | - |
Netscape 4.7x | Y | Y | - |
Netscape 6.x | * | - | * |
Mozilla 0.9.8 | - | - | * |
Mozilla 1.2.1 | - | Y | - |
Phoenix 0.5 | - | Y | - |
Opera 6.01 | - | - | Y |
Y : Tested and OK N : Tested and not OK - : Not tested *There seems to be a bug in Mozilla 0.9.8 and therefor, not surprisingly, Netscape 6.x. Making changes to the dropdown list in these browsers results in an inordinate amout of CPU usage. It works, but the browser may hang for a noticable amount of time, depending on the size of the list and the CPU speed of the PC. Mozilla 1.2.1 is fine, so I assume that Netscape 7 is also fine. |