Sunday, March 13, 2011

Placeholder emulation via Javascript

Recently I was asked to include a placeholder functionality in a form ( http://diveintohtml5.org/forms.html )
Even I prefer labels for such a task, it seems that placeholders are cooler nowadays.

The problem started when I realized that only Safari 5 and Firefox 4 support them. Between all the crossbrowser fallback replacements that I checked, none of them seemed 100% of my liking:
So, instead of rushing into the keyboard  I thought it should not be so dirty.
Luckily I came across with Tumblr and how they solved the problem. The trick is very clever and i decide to experiment with something similar.

In the beginning you have the typical  label and the input. Both surrounded by a container, that we call wrapper. This wrapper can be a <li>, <p>, <div> or whatever.
 The trick consists of placing the label behind the input via CSS (this is the reason we need a wrapper) and using Javascript to change the appearance when we write some text.

There are 3 states we should control:
- Empty (quiet) state: There is no text in the input and the label text is shown
- Focused state: The input gained the focus and the label should fade to ease the user input
- Filled state: Some text has been written in the input. We show ONLY the input text but not the label

- Error state: for completeness we can add a fourth state. When the form has been submitted and the field contains errors, we show a descriptive message and change the appearance.



Javascript is used to detect changes and transition between them. The logic is simple: it checks for focus / blur events, and if the input is empty or not. All the appearance is controlled via CSS, and javascript only has to add and remove CSS classes.

About the CSS styling there are a couple of comments:
- absolute positioning is used to place label and input in the same place.
- z-indexes are used to place the label behind the input. Note that input cannot have a totally opaque background or the label will be hidden. You can use a transparent background picture, a gradient or just declare it as transparent.
- Depending on your styles, you can spend a little time aligning the label and the input.

 One good thing of this solution is that it can be easily tunned to cope with scenarios where javascript is not enabled. Instead of having the 'wrapper' class assigned, you can add it via javascript. If it is not added, you have the usual label + input combo.

The javascript I am using is something like:

(function PlaceholderLike($,window){

    var Placeholder = function(container){
        this.container = $(container);
        this.empty = true;
        this.bind_events();
    }
    Placeholder.prototype = {
        bind_events : function(){
            var placeholder = this, check_input = function(){
                if(this.value){//'this' is the input
                     placeholder.empty && placeholder.container.addClass('filled');

                }else{
                    !placeholder.empty && placeholder.container.removeClass('filled');
                }
                placeholder.empty = !this.value;
            }
            placeholder.container.find('input').bind('focus.placeholder_like',function(){
                placeholder.container.addClass('focused');
            }).bind('blur.placeholder_like',function(){
                placeholder.container.removeClass('focused');
            }).bind('change.placeholder_like',check_input).bind('keydown.placeholder_like',function(){
                var self = this,
 // the hot values are when we are around 0 (+1 or -1 char)
                should_check = (this.value || this.value.length <= 1); 
                should_check && setTimeout(function(){
                    check_input.call(self);
                },1);
            }).triggerHandler('change.placeholder_like');
        }
    }

    $.fn.placeholder_like = function(options){
        this.each(function(){
            new Placeholder(this);
        })
        return this;
    }
})(jQuery,this);

//and it is used with a simple call 
$('.input-wrapper').placeholder_like();

P.S: While I was writting this, i discover that I was not the only that think on something similar: http://fuelyourcoding.com/scripts/infield/

No comments:

Post a Comment