Auto-populating input fields with Prototype

Yesterday I wrote a simple class which auto-populates input fields, and thought it worth sharing. I was originally inspired to write this code by Roger Johansson's post titled Autopopulating text input fields with JavaScript. While I approached the problem from a slightly different angle, I made sure to avoid the pitfalls Roger mentions.

Update —

I've written an update to this article for those interested in auto-populating input fields with MooTools.

Contents

Behaviour

  • Placeholder text should be inserted into input field upon page load.
  • Placeholder text should be targetable via CSS.
  • Clicking or tabbing into input field should remove placeholder text.
  • Placeholder text should be reinserted if input field is empty when it loses focus.

HTML5 placeholder text

HTML5 allows placeholder text to be specified in the markup through the placeholder attribute. In supporting browsers (currently Chrome and Safari) this produces the behaviour described above with no reliance on JavaScript.

Markup

<input type="search" id="s" name="s" placeholder="search..." />

Styling

input.placeholder { color: #a9a9a9 !important; }

I decided to use #a9a9a9 as Safari uses this colour for placeholder text.

Placeholder class

var Placeholder = Class.create({
    initialize: function (element) {
        this.element = element;
        this.placeholder = element.readAttribute('placeholder');
        this.blur();
        Event.observe(this.element, 'focus', this.focus.bindAsEventListener(this));
        Event.observe(this.element, 'blur', this.blur.bindAsEventListener(this));
    },
    focus: function () {
        if (this.element.hasClassName('placeholder'))
            this.element.clear().removeClassName('placeholder');
    },
    blur: function () {
        if (this.element.value === '')
            this.element.addClassName('placeholder').value = this.placeholder;
    }
});

The Placeholder class requires Prototype.

Usage

To create a new instance of the Placeholder class, simply pass the constructor a Prototype extended element:

new Placeholder($('s'));

Ensure that the DOM is ready by wrapping everything in Prototype's dom:loaded event listener. This also avoids polluting the global namespace.

document.observe('dom:loaded', function () {
    var Placeholder = Class.create({
        ...
    });
    $$('input[placeholder]').each(function (input) {
        new Placeholder(input);
    });
});

Update —

I've updated the selector used in the above example. Selecting all inputs with placeholder attributes is far more elegant than listing each input explicitly. It also means that an input added anywhere on the site will automatically receive this special treatment (provided that it has a placeholder attribute).

This site's search field shows the code in action.

Update — < time datetime="2010-04-15T12:59:00+00:00">15 April 2010

For those that would like placeholder text in password input fields not to appear as dots or asterisks in older browsers, I've written an alternative snippet. I drew inspiration from a post on iPhone-like password fields using jQuery.

// provide input hints
document.observe('dom:loaded', function () {
    var PLACEHOLDER_SUFFIX = '_placeholder'; // used for password inputs

    $$('input[placeholder]').each(function (input) {
        var label, placeholder,
            placeholder_text = input.readAttribute('placeholder');

        if (input.readAttribute('type') == 'password') {
            placeholder = input.clone();
            placeholder.type = 'text'; // not "password"
            placeholder.value = placeholder_text;
            placeholder.addClassName('placeholder');

            if (input.id) {
                // update input id and label
                placeholder.id += PLACEHOLDER_SUFFIX;
                label = $$('label[for="' + input.id + '"]')
                label.invoke('writeAttribute', 'for', input.id +
                        PLACEHOLDER_SUFFIX);
            }

            input.writeAttribute({ 'accesskey': '', 'tabindex': '' });
            input.hide().insert({ 'before': placeholder });

            // when placeholder input gains focus,
            // hide it and show "real" password input
            Event.observe(placeholder, 'focus', function () {
                this.hide();
                input.show();
                Form.Element.focus(input);
            });

            // when "real" password input loses focus,
            // if it's empty, hide it and show placeholder input
            Event.observe(input, 'blur', function () {
                if (this.value === '') {
                    this.hide();
                    placeholder.show();
                }
            });
        } else {
            // insert placeholder text
            input.addClassName('placeholder').value = placeholder_text;

            Event.observe(input, 'focus', function () {
                if (this.hasClassName('placeholder')) {
                    this.clear().removeClassName('placeholder');
                }
            });
            Event.observe(input, 'blur', function () {
                if (this.value === '') {
                    this.addClassName('placeholder').value = placeholder_text;
                }
            });
        }
    });
});

Comments

Hey dude, cool post, kind of what i had in mind here. I almost missed the Search example. thanks man for showing it to us in action too. cool!

pjammer

Just what i needed. Will be trying it out ASAP.

Password fields show ******* instead of actual text using this method.

That's default browser behaviour, unfortunately. I've worked out a solution, though. Check back in an hour. ;)

Well, that took a bit longer than expected, but I've come up with a solution. The alternative code checks to see whether the input field's type is "password". If so, it creates a new "text" input with some nifty event handlers which ensure that only one of the two inputs is visible at any one time.

Respond