Grow your CSS skills. Land your dream job.

Progressively Enhancing HTML5 Forms

Published by Chris Coyier

This is what I'm thinking is the best current way to progressively enhance forms. That is, use HTML5 features when they are available and fall back to JavaScript alternatives when they are not.

Load up Yepnope.js

<script src="scripts/yepnope.js"></script>

Yepnope is a "script loader" which will load scripts conditionally. You give it some kind of boolean value (true or false), if it's true, it'll load one set of scripts (or css), if it's false, a different set. Where do we get that boolean value? From testing a feature with...

Load up Modernizr

<script src="scripts/modernizr.js"></script>

Modernizr gives you the ability to test for HTML5 and CSS31 features. For example, with Modernizr loaded, Modernizr.inputtypes.date will be true if you are in a browser that supports inputs with a type of date property (Only Opera, since about version 9) and false if you are in a browser that doesn't support the date type.

Even better: You can get a custom build of Modernizr with only the tests you need (input types and input attributes) with Yepnope also built in in the Modernizr 2 beta builder.

Combine the two

With both of these awesome tools ready to go, we can combine their powers:

yepnope({
  test : Modernizr.inputtypes.date,
  nope : [
      // load scripts to simulate date type
  ]
});

We don't need a "yep" in this case, because "yep" means the browser supports it so we don't need any help.

What do we need for a fallback?

Since we've been using the HTML5 input type date as an example, let's think about a fallback for that. jQuery UI has a pretty sweet datepicker. jQuery is likely to help us solve a lot of fallback problems, so that's going to be a solid choice. To make the date fallback work, we'll need to load four resources:

  1. jQuery
  2. jQuery UI
  3. jQuery UI's CSS
  4. Our own script calling the datepicker

Load 'em up

With our Yepnope/Modernizr combo, that laundry list looks like:

yepnope({
  test : Modernizr.inputtypes && Modernizr.inputtypes.date,
  nope : [
	'scripts/jquery.js', 
	'scripts/jquery-ui.js',
	'css/jquery-ui.css',
	'scripts/datepicker.js'
  ]
});

Our custom script (loaded last, courtesy of Yepnope) is probably as simple as just calling the datepicker function as soon as possible:

// DOM ready function because we should probably
// be doing this in the <head>
$(function() {
	$("input[type='date']").datepicker();
});

And now we get:

Opera 11

Native support, no extra scripts loaded

Firefox 4

No native support, jQuery UI datepicker used.

Keep it going

Date type support isn't the only thing we can do here. Let's say we want to use placeholder as well. Rock'n'roll, just use another yepnope test:

yepnope({
  test : Modernizr.input.placeholder,
  nope : [
	'scripts/jquery.js', 
	'scripts/placeholder.js'
  ]
});

Notice we are using jQuery again, and thus specifying the jQuery library script. Let's say we are in a browser without support for either date or placeholder and we plan to use jQuery for both fallbacks. To do this the best way, we'll combine the tests the require jQuery into it's own yepnope block, so we don't end up loading jQuery more than once.

yepnope([
  {
    test:  Modernizr.input.placeholder || (Modernizr.inputtypes && Modernizr.inputtypes.date),
    nope: 'jquery.js'
  },
  {
    test : Modernizr.inputtypes && Modernizr.inputtypes.date,
    nope : [
	'scripts/jquery-ui.js',
	'css/jquery-ui.css',
	'scripts/datepicker.js'
  ]},
  {
    test : Modernizr.input.placeholder,
    nope : 'scripts/placeholder.js'
  }
]);

Cooler Than Polyfills

Polyfills are in the same spirit as this. They test for feature support and use native technology if possible otherwise fake it somehow. They are awesome. This is different than polyfills because we don't need to do the testing, we've already done that with Modernizr. So the scripts we load can just assume that there is no support and go from there. I think this might be better than polyfilling because:

  1. We only load two scripts at first. If a browser is super modern, that's all it will ever load. With polyfill scripts you load one for every feature.
  2. We are outsourcing feature detection to Modernizr, which is a project totally dedicated to doing that in the best ways possible. Polyfill scripts might not be as well maintained in that regard.

Or...

I haven't played with them much, but there are a couple of projects that aim to polyfill every single feature of HTML5 forms. One of them is webforms2. I kinda like the control of doing the feature testing and fallbacks myself, but there is certainly appeal to a set-it-and-forget-it approach.

 


1 Modernizr tests for other fancy web things that aren't techically HTML5 or CSS3 but are typically grouped with them, like @font-face.

Comments

  1. Jack Nycz
    Permalink to comment#

    Came to the site to look up some form styling stuff and this was your most recent post – Chris, your a mind-reader!

  2. Permalink to comment#

    Hey you can customize your Modernizr download to ship with yepnope http://modernizr.github.com/Modernizr/2.0-beta/ don’t ya know :)

  3. If I get 10 replies on this comment, I’ll donate $150 to Japan’s charity program. :)

  4. John
    Permalink to comment#

    For Japan I’ll say Hi. Long time reader never a poster. Love CSS-Tricks And hope your good on that Adel.

    Cheers.

  5. I was writing a date picker today (I’ll release it when I finish ;) and I faced this dilemma as well. Opera has a pretty decent native date picker, albeit ugly. However, Webkit just shows a lame spinner-like type of control which is definitely not user friendly. However, there’s no way to safely detect whether the date picker the browser implements is decent or just a way for them to say “we implement the date control too”, without actually providing value to authors.

    So, in my case, I chose to use the JavaScript one and fall back to whatever the native control is when JS is not available. I’m not happy that I had to do it, but thanks to @#*&^% Webkit, I think it’s the only way to go. :(

    • Please do release it! Date-pickers have always had an intense hatred of me and at this point the feeling is mutual.

      Cheers.

    • The really annoying thing is that Webkit browsers report support for the date/time input types but their implementation is completely broken. As Lea points out the control itself is pretty lame but what’s worse is that the user has to type in a value in the correct (machine readable) format or the browser will refuse to submit the form while offering no feedback at all!

      I hate to say it, but it’s kind of back to browser sniffing – anything bar Opera needs the JS date picker.

    • Modernizr reports the current webkit <input type=date> as unsupported. And I’ve gotten confirmation from the webkit engineer behind it that he will not add input validation (which will cause Modernizr to pass) until the UI is in place.

  6. Dave Allen
    Permalink to comment#
  7. Permalink to comment#

    hum the right panel of this site on this page is screwed up to the bottom
    - on topic :) I’m with Gabri

  8. iMax
    Permalink to comment#

    Hi,

    pretty good, but selected value should be blocked in field. This way we’ll protect correct value.

  9. It’d be great if every article would mention which exactly browsers support this type (or another) of customization.
    But it is good article, anyway. Thanks.

  10. This is something interesting but i have 2 questions:

    1) Let’s say that Im using a CMS like Typo3 and I add an extension that includes jQuery (undertermined version, could be older, could be newer).

    When yepnode tries to load for the first time the jQuery library, will it detect that one was already loaded, or will it load it again?

    2) Is it possible with this method to implement clever HTML5boilerplate?

    [[ lines 54-56 from index.html in http://html5boilerplate.com/ ]]

    • If you are loading jQuery anyway, you don’t need to load it with yepnope for a fallback, just remove that from the array of files to load in the “nope”. I don’t think yepnope will detect scripts loaded that it didn’t load itself.

  11. w1sh
    Permalink to comment#

    Chris Coyier is Andy McKee is Chris Coyier

    http://onemansblog.com/wp-content/uploads/2008/12/andy-mckee.jpg

    • Permalink to comment#

      haha. I have thought the same thing. I wonder which name is the alias and which name is real.

  12. Had never heard of yepnope. Beginning to use modernizr for css bits, but this combo is really slick.

  13. Don’t really understand what these codes really mean, but I’m beginning to catch some.. Thanks for the tips.

  14. Permalink to comment#

    Really smart stuff, Chris. Thanks.

  15. Every web developer should know about the Progressive enhancement and Ari technology, there are developers still make Old school mark ups and i suggest they should read this

  16. too much work. i’d rather just use the lowest common-denominator instead of doing double work and increasing the maintenance.

  17. Permalink to comment#

    I just built the datepicker fallback but I’m crashing into one nasty issue!
    Opera with HTML5 support formats the date as yyyy-mm-dd, while I need this format to be dd/mm/yyyy.

    Is it possible to adjust the format with HTML5, or should I build a JS fix on top of this?

    • YYYY-MM-DD is the ISO 8601 format. You’ll need to convert it to DD/MM/YYYY on your own. I would recommend doing it server-side if possible, but client-side just before sending the data could work.

  18. Permalink to comment#

    Good stuff!!! HTML5 are going to be wicket!!

  19. Implementing simply a datepicker on top type=date won’t work, because:

    1. HTML5 datepicker format is always yyyy-mm-dd
    2. the type=date has several features like min/max, step, valueAsNumber, valueAsDate which needs to be used for configuring the datepicker-polyfill + you have to respect dynamic changes to these properties.

    @Jeroen
    You have to “fix” this. The simplest way is to make the datepicker always use yyyy-mm-dd.

    If you need another (localized) format, you have to do this with javascript. But you cannot add this to the date-type, this type has always sent yyyy-mm-dd. If you don’t do this: newer webkit (Chrome 10+) won’t submit your form. You have to hide the input[type=date], create a new input[type=text] add the datepicker to this new input and then sync the two input-elements using the right format.

    Actually, I already have written a polyfill, which shows a localized view (default = locale of the useragent, but can be configured) and sends always correct HTML5 specified data to the server. You can find this @http://afarkas.github.com/webshim/demos/index.html .

    This said, if you want to have something similiar, to the thing HTML5 does, the tutorial here is Ok, but if you need something, that implements the right code you should use something like webshims lib. From my testing webshims lib has currently the only working forms-polyfill, which implements the new webforms stuff properly (-> accurate, according to the specification)

    regards
    alex
    (Disclosure: I’m the creator of webshims lib…)

  20. Thanks for the informative article. I’ve been using Modernizr, but hadn’t come across yepnope.js before.

    On another note, I came across a verbatim copy of this article on another site and wanted to alert you to that, in case it’s an unauthorized copy.

    The site in question is:
    www_phphosts_org/2011/03/progressively-enhancing-html5%C2%A0forms/ (replace underscores with dots – I don’t want to link to them).

  21. ever since i started on my web design journey forms have been a sore spot but with your cool teachings and html5 i am loving it! thanks chris! you are the real McCoyier! :)

  22. I have recently been doing alot of work with HTML forms for my PHP. It will be interesting to see how I can integrate this new technique to my forms.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".