Showing posts with label incremental search. Show all posts
Showing posts with label incremental search. Show all posts

Sunday, June 10, 2007

Incremental search using JavaScript

Aside from this blog, I am also the webmaster (and one of the contributors) on two photo blogs(one in English, the other in Dutch). I wrote the software for maintaining these photo blogs myself, because nothing at the time seemed to do exactly what I wanted. The upside of writing your own software is that it does exactly what I want. The downside is that if I want any new feature, I have to write it myself.

This weekend I added a simple incremental search feature to the administrative interface of the photo blogs. Nothing fancy, but just an easy way for the contributors to check for example when the last post on diaper cakes was or when we had the theme week on chocolate.

My solution is completely client-side using this JavaScript:

  • var TEXT_NODE = 3;
    var TIMER;
    var SKIP_ROW_COUNT = 2;
    var SEARCH_INPUT;

    function instrument(input, skipRows) {
    SEARCH_INPUT = input;

    if (skipRows) {
    SKIP_ROW_COUNT = skipRows;
    }

    // listen to changes to the input
    input.onkeyup = input.onchange = startTimer;
    }

    function startTimer() {
    // start (or re-start) the timer, only once it expires will we start the search
    if (TIMER) {
    clearTimeout(TIMER);
    }
    TIMER = setTimeout("search()", 500);
    }

    function search() {
    var term = SEARCH_INPUT.value.toLowerCase();
    var rows = document.getElementsByTagName("tr");
    // skip first few rows (optional author book, commands and header)
    var i;
    for (i = SKIP_ROW_COUNT; i < rows.length; i++) {
    var tr = rows[i];
    // hide row if it doesn't contain the search term
    tr.style.display = (term == "" || doesNodeContain(tr, term)) ? "" : "none";
    }
    }

    function doesNodeContain(node, term) {
    if (node.nodeType == TEXT_NODE && node.nodeValue.toLowerCase().indexOf(term) >= 0) return true;
    return true;
    }
    var child = node.firstChild;
    while (child) {
    if (doesNodeContain(child, term)) {
    return true;
    }
    child = child.nextSibling;
    }
    return false;
    }
As you can see it is pretty basic stuff. Set up some event handlers on the input, start a timer whenever the user enters some text and then do the search once the timeout expires. The search itself is done using some recursive DOM scanning. There's probably a cheaper way to do this and once I find that I'll update the script. But until then, this simple script will do just fine.

You hook this up in the HTML using:
  • <input onfocus="instrument(this, 2)" type="text">
The second argument to the instrument function is the number of rows from the top that the search should ignore. I tend to have some extra rows at the top that unfortunately don't all use th's, so this is the easiest way to exclude those from the search.

I know that putting inline JavaScript in the HTML is not very Web 2.0 and makes the site inaccessible. But since this is the administrative interface for the contributors and not the front end of the website, I think I can live with myself for not making it accessible.