JavaScript Select/List Box Filter

I've recently built a simple filter for some HTML tables and SELECT list-boxes (originally covered here). The SELECT filter is very basic in functionality:
  • Create an expanded SELECT element (size>1) somewhere on your page and give it a unique id.
  • Create a filter textbox above or below the list-box and bind the onkeyup event to trigger a filter function.
  • The filter function takes the value from the textbox and hides/removes any list-box elements that don't match.
One method of doing this is to set the display style of individual OPTION elements withing the SELECT list-box to either 'none' or 'block'. This can be done quite easily with jQuery in only 2 lines of code, namely:

MyUtil.selectFilter = function(selectId, filter) {
$("#" + selectId).find('option').hide();
$("#" + selectId).find('option:Contains("' + filter + '")').show();
}

You would bind this to your SELECT box as follows:

<b>Filter:</b> <input type="text" onkeyup="MyUtil.selectFilter('select-list', this.value)" /><br>
<select id="select-list" size="5" style="width:200px">
<option>John Smith</option>
<option>John Doe</option>
<option>Jason Bradley</option>
<option>Bob Smith</option>
<option>Jane Doe</option>
<option>David White</option>
<option>Neal Johnson</option>
<option>Richard Bradman</option>
</select>

Whenever the user starts typing in the textbox, the option elements of the list-box are dynamically hidden by setting the CSS display property to none. This works great in FireFox but gets ignored by almost every other browser (Google Chrome, IE 6/7, Safari).

None of these other browsers respect the CSS display property on OPTION elements (Chrome and IE ignore it all together, Safari hides the text but leaves an empty row in the select box).

The only workaround for this is to dynamically add/remove individual option items from the listbox instead of setting CSS properties. This can be achieved by re-writing the filter function from above to something slightly more complex:
<
MyUtil = new Object();
MyUtil.selectFilterData = new Object();
MyUtil.selectFilter = function(selectId, filter) {
var list = document.getElementById(selectId);
if(!MyUtil.selectFilterData[selectId]) { //if we don't have a list of all the options, cache them now'
MyUtil.selectFilterData[selectId] = new Array();
for(var i = 0; i < list.options.length; i++) MyUtil.selectFilterData[selectId][i] = list.options[i];
}
list.options.length = 0; //remove all elements from the list
for(var i = 0; i < MyUtil.selectFilterData[selectId].length; i++) { //add elements from cache if they match filter
var o = MyUtil.selectFilterData[selectId][i];
if(o.text.toLowerCase().indexOf(filter.toLowerCase()) >= 0) list.add(o, null);
}
}

The basic principle now is to save a copy of all the list items into a local object cache and then dynamically populate the list-box with only matching items every time the filter function is triggered. Performance appears to be comparable if not faster than the CSS method. A working example can be found here.

UPDATE: A reader commented below that the above method does not work with multi-select list boxes - i.e. if you click on a name, then use the filter and hold ctrl and click on another name, then delete the filter, the first selection will have been removed. You can read my comment response below for why that happens, or you can use the following patched version to fix it.

Comments

  1. Thanks for your post but the example page doesn't work on IE!

    ReplyDelete
  2. I like it, but can't select more than one option even when it is a multiselect list

    Steps I took,
    type "sm", Click on "John Smith", delete "sm" from search box, type "do" select "John Doe", delete "do", "John Smith" was unselected.

    Tried it again but held the "ctrl" button before selecting John Doe, but still "John Smith" was unselected
    markc09

    ReplyDelete
  3. Yup, that's because the filter actually removes any option elements from the select list that don't match the criteria, which means their selection status is also lost.

    I may update the script a little to save the status in the cache somehow and post back later...

    ReplyDelete
  4. Patched. See update at end of post with link to new version.

    ReplyDelete
  5. thanks

    ... a neat way to unselect all?

    bye

    ReplyDelete
  6. document.getElementById("select-list").selectedIndex = -1;

    ReplyDelete
  7. Like the first commenter said, doesn't work in IE.

    ReplyDelete
  8. Ok, got it to work, change list.add(o, null) to list.add(o), and make sure there's an id attribute on your option elements. Thanks again for this script!

    ReplyDelete
  9. crap, i was slightly wrong, if browser is ie, then list.add(o), otherwise, list.add(o, null), works in IE7 and Mozilla now.

    ReplyDelete
  10. Thanks for posting this script! Helped it too much at development of my project!

    ReplyDelete
  11. Does anyone know how to increase performance on this? Try implementing this with 2000 rows and it is terribly slow..

    ReplyDelete

Post a Comment

Popular posts from this blog

Wkhtmltopdf font and sizing issues

Import Google Contacts to Nokia PC Suite

Can't delete last blank page from Word