Highligh on Change

One of the more subtle, but very useful form feedback tools, is to animate the background when a user has changed a field. This animation is mostly useful with text typed inputs, however, there is a case to be made about textarea as well. Todays article shows a singleton method that manages a highlight animation, triggered whenever a user changes a text field. The developer need only register the field and the ChangeHighlighter widget will take care of the rest.

Example 1: ChangeHighlighter Singleton

Core.Controller.ChangeHighlighter = (function() {
    // local namespace
    var _F = function() {},
        _that = null,
        _YUD = YAHOO.util.Dom,
        _YUE = YAHOO.util.Event;

    // public namespace
    _F.prototype = {

        /**
         * Registers the input with this widget.
         * @method register
         * @param elem {Element} Required. The input element to listen on.
         * @param conf {Object} Optional. Configuration options.
         * @static
         */
        register: function(elem, conf) {
            var npt = _YUD.get(elem);

            // valid input
            if (npt && npt.type && text === npt.type) {
                var cfg = conf ? conf : {color: #EE0},
                    lastValue = npt.value;

                if (! cfg.duration) {cfg.duration = 0.75;} 
                
                _YUE.on(npt, blur, function() {
                    var val = (cfg.trim && .trim) ? npt.value.trim() : npt.value;
                    npt.value = val;
                    
                    if (lastValue === val) {
                        // no change, do nothing
                    }
                    // value changed, animate
                    else {
                        lastValue = val;
                        var obgColor = _YUD.getStyle(npt, background-color),
                            anim = new YAHOO.util.ColorAnim(npt, {backgroundColor: {to: cfg.color}}, cfg.duration);

                        anim._onComplete.subscribe(function() {_YUD.setStyle(npt, background-color, obgColor);});
                        anim.animate();
                    }
                });
            }
            else {
                alert(ChangeHighlighter:add Error - Invalid input parameter provided.);
            }
        },

        /**
         * Registers the input with this widget.
         * @method register
         * @param elem {Element} Required. The form element to search.
         * @param conf {Object} Optional. Configuration options.
         * @static
         */
        registerForm: function(elem, conf) {
            var form = _YUD.get(elem);

            // valid element
            if (form) {
                var npts = form.getElementsByTagName(input);

                // iterate on inputs and register
                for (var i = 0; i < npts.length; i += 1) {
                    if (text === npts[i].type) {
                        _that.register(npts[i], conf);
                    }
                }
            }
        }
    };

    _that = new _F();
    return _that;
})();

All the work is done inside of the static "Core.Controller.ChangeHighlighter.register" method, so that a closure can be leveraged each time the blur event is subscribed to. The register method requires two parameters: the input to listen on and an optional configuration. The configuration allows the developer to change the animation color, the duration, and whether or not the values of the inputs should be trimmed before comparing (this also requires that "String.prototype" has been extended with a trim method). A variable lastValue is used to track the previous value of the input so that the blur event callback knows that a change has occurred (this is why a closure is needed). Then inside the blur event callback, the lastValue is compared with the current value, and a color animation is fired if the values are not the same.

There is also a registerForm method, which simply searches for text inputs and then calls the register method for each. I avoided using any non-YUI methods for ease of use.