Appendix F. Polyfilling HTML5 with jQuery UI
In chapter 3 we discussed a number of new HTML5 elements and compared them to jQuery UI widgets. In summary, we concluded that the major advantages of the HTML5 controls are
- Ease of use.
- Dependency-free.
- The browser controls how data is inputted. (For instance, you get optimized mobile keyboards.)
The main detriments are
- You have little control over the display.
- They handle only trivial use cases.
- Only some browsers support the controls.
In chapter 3 we also had a brief discussion of which control you should use. To start, if you have a nontrivial usage scenario, you have no choice but to use JavaScript-based widgets like those of jQuery UI. If you want to build a calendar where the user can’t select weekends, you have to use a JavaScript-based datepicker—as that’s impossible to build with an <input type="date">. Conversely, if you have a trivial use case, using the HTML5 controls makes sense. You get mobile-optimized keyboards without the need to introduce a JavaScript-based control.
No matter how simple the use case, the native controls still have one big problem: browser support. Although some HTML5 elements are now widely implemented, others—like <input type="date">—are only present in a handful of browsers. But you have another option. If you want to use HTML5 controls today, and you don’t want to worry about browser support, you can use jQuery UI to polyfill the native functionality.
A polyfill is a piece of code that adds a feature when it’s not natively available on the platform. In our case, the native features we’re interested in are the new HTML5 elements and input types. To use a polyfill, first you need to detect whether the feature is supported on the platform the code is running on. The following shows a function that does a feature detect for native date support and uses the jQuery UI datepicker if native support isn’t available:

Here you first test whether the browser natively supports <input type="date"> . The check creates an <input> element and changes its type to "date". If the browser recognizes the type, it remains "date"; otherwise, the browser uses "text".
If the browser supports the native picker, you’re done. If not, you convert all date inputs to datepicker widgets . The default date format of HTML5’s date input differs from datepicker’s default. The final step is to set the datepicker’s dateFormat option equal to the specification’s format ("yy-mm-dd")
; that way, you get a consistently formatted date server-side, regardless of whether the browser natively supports the control.
If you run this code in a browser that supports <input type="date">, such as Chrome, you’ll see no visual change. In browsers with no support, such as Internet Explorer or Safari, you’ll see a jQuery UI datepicker control being used. The cool thing with polyfills is you don’t have to care about which browsers support the element and which don’t. You can rest assured that all users can use a calendar to enter a date.
To make all this possible, though, you need to accurately detect whether the user’s platform supports a given feature. And doing that can be hard; how would you know that dynamically creating an <input>, changing its type to "date", and seeing if the change took would be an accurate test for <input type="date"> support? Luckily, there’s a library that aggregates these tests for us.
Modernizr is a library that does exactly what we’re looking for: it detects HTML5 and CSS3 features in the user’s browser. It takes the guesswork out of testing for features. With Modernizr, instead of writing your own test for <input type="date"> support, you can check the Modernizr.inputtypes.date property.
You can download either a development or production version of Modernizr from http://modernizr.com/. The development version is perfect for development, as it has every check that Modernizr uses. But each of those checks takes time, and doing every check has the potential to take a long time—especially for users with slower browsers. Before using Modernizr in production, it’s a best practice to create a production build with only the checks that you need. For your purpose, you need two checks: Input Attributes and Input Types. Figure F.1 shows Modernizr’s download builder with these two checks selected.
Figure F.1. Modernizr’s production build tool at http://modernizr.com/download/. The two checks we need for polyfilling HTML5 elements are Input Attributes and Input Types.

Select additional check boxes if you need them in your application, but the input attributes and types are all you need to polyfill HTML5 elements using jQuery UI. Now that we have Modernizr in place, let’s look at the polyfills jQuery UI makes possible.
Note
You can view all these polyfills in action at http://jsfiddle.net/tj_vantoll/A62Jt/. If you view this in Chrome you’ll see all native controls—as it supports all the controls we’ll discuss—but if you open it in an older browser, such as Internet Explorer 8, you’ll see jQuery UI widgets used in place of the native controls.
Tip
There’s more to Modernizr than the handful of feature detects we need here. To learn more, check out Modernizr’s documentation at http://modernizr.com/docs/.
How to know which browser supports what
Polyfills remove the need to keep a mental list of which browsers support which features, but sometimes it’s nice to see a list. If all the browsers you support already have the native control, you have no need to use a polyfill. In my experience, the best resource for up-to-date browser feature support documentation is http://caniuse.com/, which lists support by feature in a number of tables. Support documentation on the features we’ll discuss in this appendix is at the following URLs:
- <input type="date">—http://caniuse.com/#search=input-date
- <input type="number">—http://caniuse.com/#feat=input-number
- <input type="range">—http://caniuse.com/#feat=input-range
- <progress>—http://caniuse.com/#feat=progressmeter
- <datalist>—http://caniuse.com/#feat=datalist
The first polyfill you’ll use is also the easiest, because you’ve written it before. In the following code, the only difference is your feature check for <input type="date"> support, which is now a check of Modernizr.inputtypes.date.
The code to polyfill native number <input> elements is similarly simple:
Like the spinner widget, native <input type=“number”> elements support the min, max, and step attributes to customize their behavior. But because the spinner widget automatically reads those attributes, they’re supported here without any extra work.
The range polyfill is a bit more complex, because—unlike datepicker and spinner—a slider must be created on a <div> rather than an <input>. You need to create an extra element for each <input> you need to polyfill. The approach you’ll take is shown here:

In browsers without <input type="range"> support, you loop over each <input type="range">. For each one, you create a new <div> and initialize a slider widget on it . (Remember that you can’t initialize a slider widget on the <input> itself.)
To ensure the min, max, step, and value attributes on the <input type="range"> are reflected on the <div> you create, you must explicitly read each attribute from the <input> and set them as an option of the slider . If the attribute isn’t present on the <input>, you pass the HTML5 range input’s default (0 for min, 100 for max, 50 for value, and 1 for step).
At the end of the loop, you append the newly created <div>—which is now a slider—directly after the <input> , and then hide the <input> itself
. You leave the <input> around so that it’s included in form submissions, but you hide it so the user sees only the slider. To make sure the hidden <input> maintains the correct value, the last thing you do is add a change event callback that keeps the <input> element’s value and slider’s value in sync
.
Next, we’ll look at the <progress> element, which is an element that displays the progress of a task, much like the progressbar widget. The <progress> element has two custom attributes—max and value—that work exactly like the progressbar’s max and value options.
Tip
You can learn more about the <progress> element at http://css-tricks.com/html5-progress-element/.
Modernizr core doesn’t have a check for the <progress> element. Modernizr’s download site has a noncore (Modernizr’s wording) set of checks—which includes a <progress> test—but because the check is a single line we’ll just include it inline:

To check for native <progress> support, you create a new <progress> element, and see if it has a max property defined . For browsers with support, you’re done, but for browsers without support, you then loop over each <progress> element. Like the previous slider example, you then create a new <div>. This time you initialize the new <div> with a progressbar widget
—using the custom max and value attributes from the original <progress> element
. For consistency with the HTML5 specification, if the user doesn’t provide a value attribute, you default the value option to false, which creates an indeterminate progressbar. Finally, you replace the initial <progress> element with the progressbar <div>
.
Take our tour and find out more about liveBook's features:
- Search - full text search of all our books
- Discussions - ask questions and interact with other readers in the discussion forum.
- Highlight, annotate, or bookmark.
The last polyfill we’ll look at is one for the <datalist> element. If you haven’t seen a <datalist> before, it’s a quick way of building an autocomplete that’s native to the browser. You can associate a <datalist> with an <input> by having the <input> element’s list attribute match the <datalist> element’s id attribute. The following builds a basic autocomplete:
Figure F.2 shows the display of this <input> after the user types a “j”.
Tip
You can learn more about what <datalist> elements are and when to use them at http://msdn.microsoft.com/en-us/magazine/dn133614.aspx.
For browsers that don’t support the <datalist> element, you’ll use the following code to polyfill with an autocomplete widget:

You loop over each text <input> that has a list attribute—which indicates that it’s associated with a <datalist> . For each one, you find the <input> element’s <datalist> by searching for an element that matches the <input> element’s list attribute
.
Note
You can associate <datalist> elements with other types of <input> elements such as date <input>s, and even color <input>s. To see some in action, visit http://demo.agektmr.com/datalist/. For our purposes, we’ll only polyfill the text-based version.
Assuming you find a <datalist> (that’s what the datalist.length > 0 check is for), you loop over each of its <option> elements and add them to an array of options . You then initialize an autocomplete widget on the <input>
and use that option’s array as the autocomplete’s source option. Because you don’t need the <datalist> elements to stick around in browsers that don’t support them, you remove all of them from the DOM
.
With this code in place, you can use <datalist> elements and rest assured that the user receives an autocomplete control in all browsers. There’s one last quirk to be aware of, though. Internet Explorer versions < 10 don’t recognize <option> elements unless they’re in <select> elements, meaning, this polyfill doesn’t work in those versions. Specifically, the datalist.find( "option" ) check returns nothing. The workaround for this is a bit convoluted, but it works. The fix is using Internet Explorer conditional comments to add <select> elements in the <datalist> element:
All browsers other than Internet Explorer versions < 10 completely ignore the conditional comments, including versions 10 and above. (Conditional comment support was removed from Internet Explorer in version 10.) But in versions before 10, the comments are interpreted, and a <select> is created. Having a <select> present temporarily is all you need for your polyfill to read the <option> elements. The polyfill removes the <datalist> elements entirely at the end anyway.
So unfortunately, if you need to support Internet Explorer < 10 and you want to use <datalist> elements, you must use conditional comments that insert a <select> around <option> elements for these older browsers. With this in place, the polyfill works as expected.