Cross-Browser Support for HTML5 Placeholder Text in Forms

HTML5 offers a lot of exciting new features in the realm of forms. One of my favorite new additions to forms in HTML5 is the placeholder attribute. Placeholder text is the “hint” or “example” text that resides in a text input field before the user clicks in (or tabs to) that field — and goes away the moment the field is in use. And the code is amazingly simple, too. Take this example:

 <input type="text" placeholder="Start typing to begin searching." />

The only new bit is the placeholder attribute, but it results in this:

I love placeholder text because it can help make large, complex forms easier to navigate. What better place to explain the requirements of an input field than right inside the field itself? And on short forms (like a search bar), placeholder text could be used instead of a field label, making your form that much more compact (though the W3C still suggests you use a label field).

The only real downside to placeholder text, as I see it, is I can’t use it right now … or at least, it won’t show up everywhere. Placeholder text is supported by Firefox 3.7+ and Chrome/Safari 4.0+, but Opera is late to the party and Internet Explorer is, as usual, conspicuously absent. The good news is those browsers won’t break if they encounter a placeholder attribute: they’ll just ignore it entirely. But if you were planning to use placeholders to relay important information to your users, that omission might be enough to make you call the whole thing off.

I’m a notoriously impatient person, and I don’t like to miss out on cool new functionality just because Aunt Gertrude can’t be convinced to switch away from Internet Explorer. So I suggest this as a workaround for the time being: let’s use placeholder text NOW, for all browsers that support it, and fake the same functionality for legacy browsers.

Use Placeholder Text Now

This is the easy part. For modern browsers, all we have to do is start using the placeholder attribute, and everything will work automatically. So this:

 <input type="text" placeholder="Start typing to begin searching." />

Becomes this:

Easy as pie.

All Browsers That Support It

As I said earlier, as of the time of this writing, placeholder text is supported by Firefox 3.7+ and Chrome/Safari 4.0+. However, this will inevitably change over time, so we shouldn’t rely on these data to make our implementation decisions. Besides, browser sniffing is SO 20th century. Instead, we’ll write a bit of jQuery to find out whether the user’s browser supports the placeholder attribute. This way, as soon as IE or Opera decide to come join the fun, placeholders on your site will just magically work without any additional help from us.

This code will do the trick:

 jQuery(function() {
	jQuery.support.placeholder = false;
	test = document.createElement('input');
	if('placeholder' in test) jQuery.support.placeholder = true;
});

What I’m doing here is extending the jQuery.support object, just like we did when we tested for border-radius support. I’m using JavaScript to create a new input object, and then I’m testing to see if the “placeholder” attribute is an option inside that object. It will be an option in browsers that support placeholder text, and absent in those that don’t.

Once we’ve run that code, we’ll be able to check against the jQuery.support object, like so:

 if(!$.support.placeholder) {
// Placeholder text is not supported. 
}

Fake the Same Functionality

So now we just need to get the other browsers to behave the same way. And I discovered something that will make our job easier: even if the user’s browser doesn’t recognize the placeholder attribute, the jQuery running inside that browser will still find it just fine! So we can use the placeholder element in our jQuery. This means we don’t have to do anything fancy to the input element to make it work.

The following code looks a little complicated (and it is, logic-wise), but it does the trick.

 $(function() {
	if(!$.support.placeholder) { 
		var active = document.activeElement;
		$(':text').focus(function () {
			if ($(this).attr('placeholder') != '' && $(this).val() == $(this).attr('placeholder')) {
				$(this).val('').removeClass('hasPlaceholder');
			}
		}).blur(function () {
			if ($(this).attr('placeholder') != '' && ($(this).val() == '' || $(this).val() == $(this).attr('placeholder'))) {
				$(this).val($(this).attr('placeholder')).addClass('hasPlaceholder');
			}
		});
		$(':text').blur();
		$(active).focus();
		$('form').submit(function () {
			$(this).find('.hasPlaceholder').each(function() { $(this).val(''); });
		});
	}
});

The first thing we’re doing is finding out if there’s a form element currently selected (which would be automatically selected when the page loads). If so, we store it in the “active” variable, so we can set it special later. Next, we create two event functions for our elements (I’m targeting “text” inputs, but you could expand/contract this as you need): a focus function (for when the element gets clicked in/tabbed to) and a blur element (for when the user goes elsewhere).

The blur function first checks to see if the input element has a placeholder set, and if it does, if the value of the input’s value is either blank (the user didn’t enter anything before moving on) or if it’s the same as the placeholder text. If so, we know that we’re supposed to be applying a placeholder on blur. So we set the value (content) of the input to be the same as the placeholder text, and we add a class of “hasPlaceholder” to the input so we can style it to look like placeholder text.

The focus function first checks to see if the element has a placeholder set, and if it does, if the current “value” of the input area is the same as the placeholder text. If so, that means that the user just clicked into an input field that has our fake placeholder applied. So we blank out the input’s value field and remove the “hasPlaceholder” class.

Then we do a few more housekeeping things. We’re running the “blur” function on all our fields when the page loads, which adds placeholders wherever they need to be. We then re-focus the originally active field, which would remove the placeholder from that particular field and move the cursor there. And we have a final function that fires on form submission, which strips out the value fields of any fields that have the “hasPlaceholder” class applied. This prevents us from accidentally sending along our faked placeholder copy as an actual user input.

And how do we style our input’s new faked placeholder copy to make it look like a real placeholder? It turns out that placeholder styling isn’t that complicated on average, so it doesn’t take much work:

 .hasPlaceholder {
	color: #777;
}

And with that, you have placeholders that work in ALL modern browsers, not just the fancy ones! You can see a demo here. Give it a try in various browsers.

26 Comments

  1. Sam said:

    What about password fields?

  2. Justin said:

    Wouldn’t you want to remove placeholder text from all the forms on the page, not just the first? Or rather, wouldn’t you want to remove the placeholder text from any fields belonging to the form being submitted?

  3. Rob Glazebrook said:

    Hi Sam,

    I worked a bit with password fields before I gave it up as a loss. Technically, ‘placeholder’ does work on password fields in Firefox and Chrome, though the placeholder text is obfuscated just like the password would be. I was hoping to get them to display clear text until clicked in, but the best solution for that would be to change the input type from password to text (and back again). Which changes how placeholder works, which means it isn’t a simple solution anymore. Also IE does not support changing input types. So I was forced to scale it back.

    That having been said, you can apply it to password fields, and I’m pretty sure it’ll work the same cross-browser. You just won’t be able to read the placeholder text.

  4. Rob Glazebrook said:

    Good catch, Justin. I’ll fix that in the original. After I’d written the code originally I went on to apply it in an ASP.NET setting… which means every page is wrapped in a single form. I added that line to speed up the search slightly, and forgot to remove it for demonstration purposes.

  5. Sam said:

    Actually there is a good solution http://fuelyourcoding.com/scripts/infield/ that actually works with password fields. The only drawback is that it does not play nice with Google Chrome autofill feature.

  6. wnn said:

    IE really screwed up web tech scripting. sam’s link pretty much helpful. thanks

  7. DRoss said:

    This works for me in firefox, IE – have never checked opera.

    $().ready( function() {
    $( “input, textarea” ).placehold();
    });

  8. Guillaume said:

    Nice post, very useful, just that with the help of modernizr, and the input atttribute detection you don’t need to extend the jQuery.support object, for example:

    if (!Modernizr.input.placeholder){
              $(function() {
                 if(!$.support.placeholder) {
                    var active = document.activeElement;
                    $(':text').focus(function () {
                       if ($(this).attr('placeholder') != '' && $(this).val() == $(this).attr('placeholder')) {
                          $(this).val('').removeClass('hasPlaceholder');
                       }
                    }).blur(function () {
                       if ($(this).attr('placeholder') != '' && ($(this).val() == '' || $(this).val() == $(this).attr('placeholder'))) {
                          $(this).val($(this).attr('placeholder')).addClass('hasPlaceholder');
                       }
                    });
                    $(':text').blur();
                    $(active).focus();
                    $('form:eq(0)').submit(function () {
                       $(':text.hasPlaceholder').val('');
                    });
                 }
              });
            } 

    Thanks

  9. DRoss said:

    Duh – I forgot to include the placeholder script. And actually yours might be better — or at least slimmer:

    jQuery(function(){
    $.fn.placehold = function( options ) {
    var opts = $.extend( {}, $.fn.placehold.defaults, options );

    return this.each( function() {
    if ( !( “placeholder” in document.createElement( “input” ) ) ) {
    var placeholder_attr = $( this ).attr( “placeholder” );

    if ( placeholder_attr ) {
    var elem = $( this )

    if ( !elem.val() || elem.val() == placeholder_attr ) {
    elem.addClass( opts.placeholderClassName ).val( placeholder_attr );
    }

    elem.focus( function() {
    if ( elem.val() == placeholder_attr ) {
    elem.removeClass( opts.placeholderClassName ).val( “” );
    }
    });

    elem.blur( function() {
    if ( !elem.val() ) {
    elem.addClass( opts.placeholderClassName ).val( placeholder_attr );
    }
    });

    elem.closest( “form” ).submit( function() {
    if ( elem.val() == placeholder_attr ) {
    elem.val( “” );
    }

    return true;
    });
    }
    }
    });
    };

    $.fn.placehold.defaults = {
    placeholderClassName: “placeholder”
    };
    });

  10. Victor said:

    Opera 11 supports this attribute. :-D

  11. Patrik Zingmark said:

    I did a jQuery plugin for this a couple of years ago. http://plugins.jquery.com/node/11997

  12. Keegan Street said:

    Hey Rob,

    Thanks for sharing. That’s a useful snippet. I’d recommend changing the input selectors to $(‘:text, textarea’) to add textarea support. The :text selector only selects inputs not textareas.

    Cheers,
    Keegan

  13. Krista said:

    Thanks for sharing this information. I am just catching up with some of the latest techniques for design/dev.

  14. Carl said:

    In FF 3.6.13 on Mac (and probably earlier) this will not work, as placeholder is not yet supported and the value reported by the browser for .attr(‘placeholder’) is not “” but instead is undefined.

    The problem only presents on submit when inputs do not have a placeholder attribute defined (which is not included in the demo)

    To fix you need this in the 2 appropriate places in the code:

    if ($(this).attr(‘placeholder’) != ” && $(this).attr(‘placeholder’) != undefined …

    Sorry if I’m sounding terse. This code ended up in a project I’m working on and I didn’t know it was there and it took me about a day to figure out why my basic form was deleting the text fields on submit. Brain = fried

  15. On May 25, 2011
    10:32PM

    Clinton Green said:

    Thanks, this helped a lot with compressing forms with HTML5 and then hacking it to work in IE.
    Cheers

  16. On May 27, 2011
    8:32AM

    Riak said:

    Thanks for sharing this. Im using place holder on my current site (still being created) Im just going to scrap place holder and use a classic javascript version.

  17. On May 27, 2011
    9:56AM

    Lindsey said:

    Thanks so much for making this available! i actually don’t know ANYTHING about jquery and was able to implement with no problems- also thanks to all who commented! –the bit about changing input selectors to $(‘:text, textarea’)really helped me out also–

  18. On July 17, 2011
    3:04PM

    George said:

    Carl, you are a hero. The exact thing happened to me. The code in this article has a horrible flaw that in some browsers (including IE9) this piece of code clears type=text input fields on the page when submitting. Spent two hours of my life trying to find the problem, and I am lead here. This needs to be fixed with Carl’s suggestion. Sloppy testing by OP.

  19. On August 03, 2011
    10:11PM

    Alice Bevan-McGregor said:

    Here’s an even more packed version, also using modernizr:

    ${function(){var p=’placeholder’;if(!Modernizr.input[p]){var z=’input[',y=']‘;$(z+p+y).map(function(){var t=$(this);if(t.val()==”)t.val(t.attr(p));});$(z+p+y).focus(function(){var t=$(this);if(t.val()==t.attr(p))t.val(”);}).blur(function(){var t=$(this);if(t.val()==”)t.val(t.attr(p));})}});

    Haven’t tested this, though.

  20. Pascal said:

    Thanks. Work’s for all Forms but for some reasons not with the TYPO3 Indexed Search Form. I think there are other JavaScripts which are fighting this one. Just want to inform others in case they run in the same problem. My workaround was to do the replacements only on forms I wanted. So replace these lines
    $(‘:text’).focus(function () {
    with
    $(‘.yourForm :text’).focus(function () {

    and
    $(‘:text’).blur();
    with
    $(‘.yourForm :text’).blur();

    and
    $(‘form’).submit(function () {
    with
    $(‘.yourForm’).submit(function () {

    Cheers,
    Pascal

  21. Tess said:

    How about this simplification:

    test = document.createElement(‘input’);
    jQuery.support.placeholder = ‘placeholder’ in test;

  22. dunhakdis said:

    Thank you for this script.

    For textarea and password support please use this script.

    Tested in IE8, IE9, FF 3.5+, and G Chrome:

    jQuery(function() {
    jQuery.support.placeholder = false;
    WebKit_type_browser = document.createElement(‘input’);
    if(‘placeholder’ in WebKit_type_browser) jQuery.support.placeholder = true;

    if(!$.support.placeholder) {
    var active = document.activeElement;
    $(‘:text, textarea, :password’).focus(function () {
    if ($(this).attr(‘placeholder’) != ” && $(this).val() == $(this).attr(‘placeholder’)) {
    $(this).val(”).removeClass(‘hasPlaceholder’);
    }
    }).blur(function () {
    if ($(this).attr(‘placeholder’) != ” && ($(this).val() == ” || $(this).val() == $(this).attr(‘placeholder’))) {
    $(this).val($(this).attr(‘placeholder’)).addClass(‘hasPlaceholder’);
    }
    });
    $(‘:text, textarea, :password’).blur();
    $(active).focus();
    $(‘form’).submit(function () {
    $(this).find(‘.hasPlaceholder’).each(function() { $(this).val(”); });
    });
    }

    });

  23. Ryan said:

    Awesome chunk of code!!! Thanks!!

  24. Sandeep Singh said:

    The code is not working in IE9

  25. Elizabeth Kanz said:

    This is great, and I have it working. Question though- I’m using a form where the users will select a type of form they need to fill out, and the form loads on the page in an HTML panel from another html page. When this loads into the panel, the placeholder text doesn’t work, but when I view the form page on it’s own, it works. I thought I might just need to add the script to the page that I’m paneling in the form into, but it still doesn’t work. Any ideas?

  26. Abdul said:

    Place holder is not working in IE 9

9 Responses Elsewhere

  1. 180+ HTML5 Tutorials and a Round-Up of HTML5 Round-Ups - WebsitesMadeRight.com said:

    [...] Cross-Browser Support for HTML5 Placeholders (CSS Newbie | Nov 29, 2010) [...]

  2. 180+ HTML5 Tutorials and a Round-Up of Round-Ups - WebsitesMadeRight.com said:

    [...] Cross-Browser Support for HTML5 Placeholders (CSS Newbie | Nov 29, 2010) [...]

  3. 270+ HTML5 Tutorials and a Round-Up of Round-Ups - WebsitesMadeRight.com said:

    [...] Cross-Browser Support for HTML5 Placeholders (CSS Newbie | Nov 29, 2010) [...]

  4. Erreur 500 » Blog Archive » Attribut placeholder des anciens aux nouveaux navigateurs said:

    [...] que le browser comprend l’attribute placeholder ou pas. J’ai trouvé ce code sur ce site. La deuxième partie s’occupe du comportement “clear on focus” dans le cas ou [...]

  5. HTML5 Placeholders for troublesome browsers (ie. IE9) | Femgeek.co.uk said:

    [...] Tagged cross browser compatability, html5, placeholdersAdd a CommentI originally found this code here but it didn’t support textareas, so I’ve added that in.Just add this javascript to your [...]

  6. Заставляем работать placeholder в IE | Полу-блог said:

    [...] пользователю некоторые неудобства. На сайте cssnewbie.com я нашел статью, где описано как сделать [...]

Leave a Comment