HTML5Feature

Make HTML5 work today with polyfills

Want to use modern web features but need to support older browsers? Polyfills might be the solution. Jason Johnston fills in the essential details

This article first appeared in issue 238 of .net magazine – the world's best-selling magazine for web designers and developers.

We’ve all been in this situation: you’re coding a website and something in its design or behaviour would be so easy to implement if you could use a certain modern HTML5 or CSS3 standard feature. But your requirements dictate full support back to IE7, so you’re stuck either (a) forking your code by browser, or (b) ditching the standard entirely and using some bloated, hacky, error-prone workaround to replicate the functionality.

Fortunately there is a middle ground that has become increasingly popular: polyfills. This term refers to a specific type of shim that implements a standard web feature or API in browsers that lack support natively.

Polyfills are unobtrusive

Except for the code to include it in your page, a true polyfill has no API of its own – it simply uses the same syntax or API as the standard feature it implements. This differs from libraries such as jQuery, which attempt to smooth out browser differences by providing an abstracted API above the standard one.

Flexie gives an early version of CSS3 Flexible Box Layout – but not the final syntax

The advantage of a polyfill is that because there is no additional API for you to worry about learning or debugging, you can focus on writing only lean, standards-based code, and use all the time you save to focus on more important things!

Polyfills are temporary

The ultimate goal of any polyfill is to become obsolete over time. As more and more browsers start to implement the missing feature, and the number of browsers needing to be polyfilled shrinks to zero, the polyfill will gradually stop being downloaded or used altogether. This happens automatically without you having to change your site’s code later on.

For this automatic obsolescence to work, you must make sure that you are including the polyfill only in those browsers that need it. We will be returning to this later, in the section of the article that deals with conditional loading. It’s natural to think of polyfills as tools for improving the experience in outdated browsers, since that’s where they do their work. But it’s better to think of them as tools for improving the experience in modern browsers, while old ones are secondary.

Why is that? Well, since polyfills facilitate writing plain standard code as the baseline, the modern browsers that support it will benefit from drastically smaller file sizes, faster rendering and well-integrated native implementations. The old browsers would be downloading and running extra shim code regardless of whether you chose another library or shim, but a polyfill prevents the capable browsers from having to do the same.

In short, polyfills enable you to stop hurting your users who come to your site in modern browsers, without leaving the others behind.

Polyfills aren’t just for old browsers

Most of us think of polyfills as tools for ‘Old IE’ (versions before 9). This is understandable; for much of the last decade the primary target for polyfills has indeed been old versions of IE, owing to its severe lack of support for numerous modern standards.

Find polyfills! Here’s the definitive list

But in the last year or two we’ve seen a very interesting development: polyfills are moving beyond old browsers and targeting modern engines as well – even the latest versions! This is happening because new HTML5 standards are developing so quickly that the browser makers can’t keep up, and forward-thinking developers who want to adopt them are creating polyfills to bridge the gap.

A good example is the CSS.supports polyfill, which implements the CSS.supports JavaScript API from the CSS3 Conditional Rules Module working draft. Although no browsers currently shipping implement this API, it is expected to be supported in future, so this polyfill lets developers start using the API today. In theory, once browsers start shipping with a native implementation, that will take over seamlessly and the polyfill will stop being used.

But beware: using features based on an unfinished specification is inherently risky! This is the case whether you choose to polyfill them or not – if the spec changes, your code won’t work when the final native implementation comes along. We’ve already seen this happen, for example, with the CSS3 Flexible Box Layout module, which has changed drastically so that sites using the old draft syntax along with the popular Flexie polyfill will not work in browsers that implement the final syntax without code changes. A good rule of thumb: unless you are willing to diligently update your site’s code along with spec changes, you should avoid using draft features.

How do you use a polyfill?

There are several different methods by which polyfills can be applied. You should obviously consult the documentation for the particular polyfill you wish to use, but most of them use either script includes or HTC behaviors.

Script includes

Jason Johnston’s own CSS3 PIE make IE6 to IE9 capable of rendering CSS3 properties

The most common method is to simply include one or more JavaScript files into the page; the script then does its work automatically without additional code. Polyfills included this way often add global objects or methods, parse and rewrite the page’s CSS, or wait for page load and manipulate the DOM.

For example, Lea Verou’s popular -prefix-free polyfill enables support for several un-prefixed CSS properties in browsers that only support the vendor-prefixed versions. To use it, you need to download the prefixfree.min.js file and include it in your page immediately after your style sheets:

<script src="/path/to/prefixfree.min.js"></script>

That’s all there is to it: once included, the script reads the page’s CSS files, parses them looking for any of the supported non-vendor-prefixed CSS properties, and rewrites them using the appropriate vendor prefix for the current browser. For example, in your CSS you can write just the standard box-shadow property:

.myClass {
  box-shadow: 0 2px 5px rgba(0, 0, 0, .5); 
}

…and when a user visits the page with, for instance, an older Firefox browser, that will be rewritten by the polyfill as:

.myClass {
  -moz-box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
}

HTC behaviors

Several polyfills that specifically target Internet Explorer make use of HTC (HTML Component) behaviors. This is a proprietary technology, only available in IE9 and below, that essentially runs a custom script on elements that match a specific CSS selector. See here if you’re curious about how behaviors work.

As an example, the CSS3 PIE polyfill (disclaimer: I created it) uses an HTC behavior to render some of the most common CSS3 box decoration properties in IE6 through 9. It is applied by adding the behavior property to your CSS3-styled elements:

.myClass {
  border-radius: 8px;
  box-shadow: #666 0px 2px 3px;
  behavior: url(/path/to/PIE.htc);
}

This tells IE to load the PIE.htc file and execute it for each element that matches the .myClass selector. The script within PIE.htc then sees the border-radius and box-shadow CSS3 properties are present, and renders a rounded box shape and a soft drop shadow using VML (Vector Markup Language, another proprietary IE technology.) The end result is generally indistinguishable from modern browsers.

An HTC behavior has some distinct advantages over a normal script file: it can execute before the full page is loaded, it reacts automatically to dynamic addition and removal of page elements, and since non-IE browsers ignore the unknown behavior CSS property, it provides conditional loading by its very nature.

However there are some gotchas: the HTC file must be in the same domain as the page using it, the server must send it using the correct Content- Type HTTP header, and the behavior property resolves relative URLs based on the HTML page rather than the CSS file location. These can make using the polyfill trickier, so keep them in mind.

Modernizr enables you to download only the specific feature tests you need

Conditionally loading polyfills

To ensure that modern browsers aren’t downloading unnecessary files, it is essential that you use some sort of conditional loading for your polyfills. Most of them provide instructions on doing this in their documentation, but here are some general options:

Conditional comments

If the polyfill targets IE browsers only, you can surround its script include in conditional comments. For example:

<!–[if lt IE 9]>
<script src="path/to/html5shiv.js"></script>
<![endif]–>

Additional JavaScript

If you need something more intelligent than a simple IE version test, you can write a small bit of JavaScript to conditionally include files. For example, the following code includes the json2.js polyfill script only if the global JSON object is not already provided natively by the browser:

<script>
if (!window.JSON) {
  document.write('<script src="path/to/json2.js">
<\/script>');
}
</script>

Asynchronous loading

If the polyfill isn’t needed immediately during page load, you might consider loading it asynchronously instead to improve perceived performance (see here):

<script>
if (!window.JSON) {
  (function(d, t) {
    var g = d.createElement(t), s =
    d.getElementsByTagName(t)[0];
    g.src = 'path/to/json2.js';
    s.parentNode.insertBefore(g, s);
  })(document, 'script');
}
</script>

Modernizr.load

Beyond simple object detections like this, feature testing can get very complicated. This is where Modernizr comes in handy, with its extensive library of feature tests. It also includes a more powerful conditional loading library called yepnope, which gives extra control over the load process and can handle multiple JS or CSS files.

For example, if you need to store persistent client-side data, HTML5’s localStorage API is a logical choice. Fortunately there are a number of polyfills that emulate it in older browsers. First, go to www.modernizr.com/download and select the localStorage feature test and Modernizr.load. Generate and download the result, place it in your site directory, and include it in your page:

<script src="/path/to/modernizr.custom.12345.js"></script>

Now grab the storage.min.js polyfill and upload it to your site directory.

Finally, let’s use the Modernizr.load method to test for the presence of native localStorage and load the storage polyfill if it is not supported. We’ll also make use of Modernizr.load’s ‘complete’ callback to add some default data to the localStorage once it’s available and/or polyfilled:

<script>
Modernizr.load({
  test: Modernizr.localstorage,
  nope: '/path/to/storage.min.js',
  complete: function() {
    if (!localStorage.getItem('firstRunTime')) {
      localStorage.setItem('firstRunTime', '' + new Date());
    }
  }
});
</script>

Keep in mind that Modernizr.load is definitely a power tool and might not be right in all situations. If you are considering using it, you should weigh the total file size of the polyfills you intend to use against the size of the custom Modernizr build file. The latter is loaded by all browsers, so if it is larger than the polyfills themselves, you’d actually be better off without conditional loading at all, or using one of the simpler methods described above.

www.html5please.com assesses HTML5 features and lists available polyfills

How do polyfills work?

While the detailed inner workings vary greatly, polyfills typically do one or more of the following things behind the scenes:

1. Create new global JavaScript objects
For APIs that are exposed in global objects, like window.JSON or window.localStorage.

2. Add properties/methods to object prototypes
For APIs that are additions to existing objects, like Array.prototype.forEach().

3. Execute plug-ins
For things that can’t be emulated with other built-in browser features, such as HTML5 <video>.

4. Parse and rewrite CSS
To interpret CSS selectors or properties that the browser doesn’t recognise for itself.

5. Query and modify the DOM
To change the end result seen by the user.

Also, nearly all polyfills rely on JavaScript to work their magic, so be aware that users with JavaScript disabled will not benefit.

Testing with polyfills

While polyfills try to make things work identically across browsers, don’t think that by using them you can avoid thorough testing! On the contrary, having good processes and tools for cross-browser/ cross-platform testing is as essential as ever.

As always, test the page using the version of the browser that your visitors will be using. If you can, get this info from your site – the best stats are local!

browserstack.com provides accurate, interactive online testing for numerous browsers and OSes

I recommend using several virtual machines (running in VirtualBox, VMware, or similar) containing different OS and browser versions. This is particularly important for IE testing, where you really need to have one version of IE per OS install. Avoid using tools such as IETester that attempt to run multiple versions of IE together on the same OS install – they are not 100 per cent accurate. Also, never rely on the Browser Mode switching in IE’s developer tools, which is even less accurate.

If you are unable to maintain virtual machines, you can use an online testing tool. Make sure that it is an interactive tool, not one that simply takes screenshots, because those often don’t capture things at the right time and don’t give you any sense of performance. I recommend BrowserStack: it isn’t free, but it is the most accurate such tool I’ve seen.

In conclusion

Polyfills are powerful tools that help save development time and costs, improve your site’s code, and enhance the user experience in modern browsers. They’re not the right tool for every job, but they’re a good thing to have in your toolbox – and they will be around long into the future!

Log in to Creative Bloq with your preferred social network to comment

OR

Log in with your Creative Bloq account

site stat collection