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.