Channels ▼

Eric Bruno

Dr. Dobb's Bloggers

Real-Time List Filtering in HTML5/JavaScript

August 12, 2013

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

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Dr. Dobb's TV