Real-Time List Filtering in HTML5/JavaScript
A little over a year ago, I wrote a blog with code that demonstrates how to easily search for an item in a listbox, where the total list of items are narrowed down as you type. The contents of the listbox are filtered to contain only those items that contain the text you're typing, with each keystroke, regardless of case.
This is a neat little trick because the contents of the list are filtered and updated in real-time as you type, and it even supports the backspace key, adding back items as you delete characters from your search. That blog illustrated this technique using Java and JavaFX. I recently rewrote this code in JavaScript to work within an HTML5 web application. Below is how the list looks in its initial state, containing all of the entries:
Let's imagine there are hundreds of items in this list, making it difficult to scroll through the list to find one item of interest. To keep this example simple, however, there are only 10 entries. Let's begin by examining the code that handles keystrokes. First, add a key handler for the input textbox when the browser loads the web page:
window.onload = function() { // Look for input box and insert key handler var prevTxt = null; var txt = document.getElementsByName('filterTxt'); if ( txt != null ) { txt[0].onkeyup=function(event) { var e = event || window.event; var curTxt = txt[0].value; handleKeyPress(prevTxt,curTxt); prevTxt = curTxt; return true; } } }
The handleKeyPress()
function will now get called whenever a key is pressed within the "filter" input box. This is where the magic happens. First, the original list of entries within the listbox is saved (this happens only once):
// Store the original list of entries to restore when backspacing if ( originalentries === null ) { originalentries = new Array(); for ( c = 0; c < select.children.length; c++ ) { originalentries.push(select.children[c]); } }
Next, a check is made to see if the user hit the backspace key. If so, the list's entries are restored to the original set of entries. Filtering will take place all over again with the shortened filter text in a moment.
// If the number of characters in the text box is less than last time // it must be because the user pressed delete if ( oldVal !== null && (newVal.length < oldVal.length) ) { // Restore the lists original set of entries // and start from the beginning for ( c = 0; c < originalentries.length ; c++ ) { select.add(originalentries[c]); } }
Next, each word typed into the filter input box is separated using the split()
function. Each listbox entry that does NOT contain all of the words entered (regardless of order) is placed in a list of entries to be removed from the list box:
// Break out all of the parts of the search text // by splitting on white space var parts = newVal.split(' '); // Filter out the entries that don't contain the entered text var toremove= new Array(); for ( i = 0; i < select.children.length; i++ ) { var entry = select.children[i]; var match = true; var entryTxt = entry.text; for ( p = 0; p < parts.length; p++ ) { // The entry needs to contain all portions of the // search string *but* in any order var part = parts[p].toUpperCase(); if ( entryTxt.toUpperCase().lastIndexOf(part) < 0 ) { match = false; break; } } if ( match == false ) { toremove.push(entry); } }
Finally, the entries marked for removal are actually removed from the list box:
if ( toremove != null ) { for ( t = 0; t < toremove.length; t++ ) { var entryTxt = toremove[t].text; select.removeChild(toremove[t]); } }
In action this appears to be complex, but the code works quite simply. Here are the general steps (in a slightly different order than in the code):
- Loop through all the items in the list
- Check each item to see if it contains the text entered in any order. Example: typing "Bruno Eric" matches the listbox entry "Eric Bruno" and will allow it to remain in the list.
- If the user hits the backspace key, restore the list to its full set of original entries, and search from the top again
The screenshot below shows the listbox after typing the text "Br":
This is a simplified example of a real-world scenario where it can often be helpful to provide flexibility when searching through long lists of text. And, this flexibility need not be difficult to implement. Here is the code for a full HTML/JavaScript example you can run yourself.
Happy coding!
-EJB