Make your search boxes (inputs) smarter with Javascript: Search_Box behavior +

Almost every new web site has (or should have) a search box. User skills are improving and people know the basics of using search and use it more often then before. So, your site has a search box (or it’s gonna have one pretty soon).

Search boxes are useful (but boring) by default

The search box is much nicer and much more useful if it has a default value that can act as many different things: a call to action, an explanation, a teaser of any kind, or just as an outlet for your witty messages. However, once you’ve set a default value, your users are forced into clearing the value manually before actually being able to use the thing. Putting extra work on our users is always a no-no, and as an added bonus we get to have some Javascript fun!

OK, but why?

Because it’s fun! And because I’ve implemented my fair share of those damn search boxes, and implementing the same thing over and over again made me feel stupid. I got tired of it and whipped this thing up so I don’t have to ever think about the issue: I just include the script where it’s needed and voilà!

Another thing that I find annoying is that by default browsers do not select the field contents once the field is focused (in forms, but they do select the contents if you focus a location bar for example) — at least on Windows — so I decided to fix that too.

Onfocus, onblur – say what?

Using onfocus and onblur event handlers we can add behavior to our search box. Here’s the logic: if the search box receives focus, we’re gonna clear the value of the box (only if the current value is the same as default, or if it’s blank), and when the input looses focus (onblur) we’re gonna set the default value back (only if the user hasn’t changed the value). It might sound confusing, but you can check the search box in the sidebar to see what I’m talking about.

The examples

Some obligatory examples:

The script

The fully commented source is listed below, and a minified version is also available. It’s free to use however you see fit (the MIT License).

It has been tested and confirmed working on all of the following:

  • Windows XP / Firefox 2.0.0.11
  • Windows XP / Safari 3.0.4 (beta)
  • Windows XP / Opera 9.25
  • Windows XP / IE 6.0
  • Windows Vista / IE 7.0
  • Mac OS X 10.5 Leopard / Safari 3.0.4
  • Mac OS X 10.5 Leopard / Firefox 2.0.0.11

If anyone has a chance to test on other platforms/browsers and finds a bug or quirk or something, let me know.

Known issues:

  • Selecting focused field’s contents using select() is acting strangely in Safari (across all the platforms) — looking into it
/**
* Search_Box behavior
* @author   zytzagoo
* @version  0.2
*/
function Search_Box(cfg) {
    // defaults are always nice
    var defaults = { ELEMENT_ID: 'q', DEFAULT_VALUE: 'inherit', FOCUSED_VALUE: '' };
    if (cfg) {
        // we have a cfg, loop thru the properties
        // and make sure something is not missing
        // if so, add it from the defaults
        for (var name in cfg) {
            if (cfg.hasOwnProperty(name)) {
                for (var defname in defaults) {
                    if (defaults.hasOwnProperty(defname)) {
                        if (!(cfg[defname])) {
                            cfg[defname] = defaults[defname];
                        }
                    }
                }
            }
        }
    } else {
        cfg = defaults;
    }
    /**
    * return a new object literal with extra
    * stuff attached on it
    */
    return {
        /**
        * Checks the element we're working on exists, and
        * attaches handlers to it. Usually called after the document
        * has loaded or (even better but harder to achieve truly
        * cross-browser) when the element referenced by
        * Search_Input.ELEMENT_ID is available in the DOM.
        */
        init: function () {
            var el = document.getElementById(cfg.ELEMENT_ID);
            if (el) {
                /**
                * special case: 'inherit'
                * This resets the passed in default value and
                * if the element has a previously set value, that
                * value is used as the default from now on.
                */
                if (cfg.DEFAULT_VALUE === 'inherit') {
                    cfg.DEFAULT_VALUE = '';
                    if (el.value !== '') {
                        cfg.DEFAULT_VALUE = el.value;
                    }
                }
                /**
                * If a default value is specified, override
                * whatever exists in the value attribute of the input in html
                */
 
                /**
                * if we have a custom focus handler passed in,
                * attach that one too and make sure it is called first
                */
                if (cfg.focus) {
                    Search_Box.attach_handler(el, 'onfocus', cfg.focus);
                }
                // our own focus handler is always attached
                Search_Box.attach_handler(el, 'onfocus', this.focus);
                /**
                * same as above except this takes care of onblur handlers
                */
                if (cfg.blur) {
                    Search_Box.attach_handler(el, 'onblur', cfg.blur);
                }
                // our own onblur handler is also always attached
                Search_Box.attach_handler(el, 'onblur', this.blur);
                /**
                * in case the elem has no current value,
                * set it to the specified default
                */
                if (el.value === '' || (cfg.DEFAULT_VALUE && cfg.DEFAULT_VALUE !== '')) {
                    el.value = cfg.DEFAULT_VALUE;
                }
            } else {
                throw new Error('Search_Box.init: element (id: "' + cfg.ELEMENT_ID + '") doesn\'t exist');
            }
        },
        /**
        * Handles the onfocus event of the element
        */
        focus: function (e) {
            // delegate, passing in the event object
            var t = Search_Box.get_target(e);
            // if the target of the event is an input element
            if (t.nodeName.toLowerCase() === 'input') {
                // if the value of that input is empty or default
                if (t.value === cfg.DEFAULT_VALUE || t.value === '') {
                    // set the value to the specified focused value
                    t.value = cfg.FOCUSED_VALUE;
                    /**
                    * if the now set focused value is not empty
                    * select the contents of the box
                    */
                    if (t.value !== '') {
                        t.select();
                    }
                }
            }
            return true;
        },
        /**
        * Handles the onblur event of the element
        */
        blur: function (e) {
            // delegate, passing in the event object!
            var t = Search_Box.get_target(e);
            // if the target of the event is an input element
            if (t.nodeName.toLowerCase() === 'input') {
                /**
                * if the current value of that element is the
                * focused value or empty, set the current
                * value to the specified default value
                */
                if (t.value === cfg.FOCUSED_VALUE || t.value === '') {
                    t.value = cfg.DEFAULT_VALUE;
                }
            }
            return true;
        }
    };
}
/**
* Gets the target of an event
*/
Search_Box.get_target = function (x) {
    x = x || window.event;
    return x.target || x.srcElement;
};
/**
* Attaches event handlers to an existing object.
* If an existing handler is found, it is executed before
* our newly attached handler
*/
Search_Box.attach_handler = function (o, evt, f) {
    if (o !== null) {
        var existing_handler = o[evt];
        if (typeof o[evt] !== 'function') {
            /**
            * no previous handler found
            * TODO: this might need looking into,
            * but it seems to work so far...
            */
            o[evt] = f;
        } else {
            /**
            * Previous handler found, invoke it,
            * while making sure that the 'this' keyword
            * inside the handler function refers to the
            * input element.
            * This enables some cool custom onfocus and
            * onblur handlers possible and logical and
            * easy to develop and not worry about naming
            * stuff...
            */
            o[evt] = function (e) {
                existing_handler.apply(o, arguments);
                f.apply(o, arguments);
            };
        }
    }
};

16 Responses to “Make your search boxes (inputs) smarter with Javascript: Search_Box behavior”

  1. Wooha, this openID works!

  2. Gee wiz, very neat zyt!

  3. @medo: :blush Thx :)

  4. tldr! :D

  5. Nice!

  6. this is cool, thnx zyt!

  7. sweet :)

  8. Very cool and thanks for posting this! I am trying to get
    Example 5 to work, but am not having luck. I noticed you
    have this line in your code:

    is it required to get Example 5 to go?

  9. @Stephen: I guess WordPress cut off the code you entered in the comment :/ Which line of code were you talking about? And in which browser are you trying to run it?
    P.S.
    I noticed I had some copy/paste errors in Example 5 — I’ve fixed that now, but it was just cosmetic.

  10. Ne bi bilo lose boju js keyworda (if, return, typeof) prilagoditi boji pozadine da text bude citljiviji :).

    Evo sto mi vraca Colour Contrast Analyser

    Result – colour/brightness:
    The difference in brightness between the two colours is not sufficient. The threshold is 125, and the result of the foreground and background colours is 17.

    The difference in colour between the two colours is not sufficient. The threshold is 500, and the result of the foreground and background colours is 125.

    Result – luminosity:
    Fail (The contrast ratio is: 1,03)
    Text or diagrams and their background must have a luminosity contrast ratio of at least 5:1 for level 2 conformance to guideline 1.4,and text or diagrams and their background must have a luminosity contrast ratio of at least 10:1 for level 3 conformance to guideline 1.4

  11. @tm: Hvala. Ovo je default kako je došlo s nekim WP pluginom za syntax highlight s kojim se nisam detaljnije pozabavio. Al budem :)

  12. BTW, special thanks to Stephen for pointing out a parse error in the source posted above. It should be fixed now.

    It was due to WP’s conversion of & to &, which made line 80 into non-valid Javascript.

  13. very nice. misses just one parameter DEFAULT_COLOR wich would be color for default text. onfocus color shud by darker with more contrast.

  14. @dux: Woot! That’s a great suggestion, thanks!

  15. @dux: After thinking about it for some time, there’s no need to code what you suggested directly in the script. It’s already possible with custom focus and blur handler functions (I knew they’d come in handy!). Check out Example #6 for a demo.

  16. Thanks a lot for choosing an open source license!