Making HTML5 and CSS3 work with polyfills and shims

Rey Bango, evangelist at Microsoft and director of community for the jQuery project, explains how to make HTML5 and CSS3 features work in older browsers with the help of polyfills and shims

Shares

I love HTML5. I mean I REALLY love HTML5. The newest version of HTML offers so many new and cool features that its impact on our future development is unquestionable. It’s understandable why so many developers are feverishly adding new bits into their web applications and anxiously waiting for new features, like WebSockets and IndexedDB, to mature and get included in the major browsers.

And the browser vendors are listening. It sometimes feels like a leapfrog effect to see which browser can support the most features. And yes, even Microsoft, whose IE6 browser has been the bane of many developers, has jumped into the standards bandwagon, fully endorsing HTML5 much to the delight and relief of the developer community.

Older browsers

But while developers are all clamouring to get their hands on the great features included in HTML5 as well as the new version of CSS, CSS3, there’s an important issue that needs to be considered. Non-modern browsers don’t support many HTML5 and CSS3 features. Doh!

To add to this, browser fragmentation has caused a varying level of support for these features even within specific browsers, making it incredibly difficult to simply code and forget.

An example of this is how Firefox v4.0 supports the WebM video codec for rendering video using the new HTML5 <VIDEO> tag while version 3.6, which also supports the new <VIDEO> tag, can only render in Ogg/Theora. This type of scenario puts added pressure on developers who are trying to leverage new HTML5 features in a way that is maintainable, scalable and cross-browser. Yeah, that’s the pink elephant in the HTML5 party.

It brings up the question, “How do we use HTML5 & CSS3 today, while still supporting non-modern browsers?”. Thankfully, there’s an answer and solution.

Filling the gaps

Luckily, there are a ton of very smart developers who have tried to tackle this and created solutions called polyfills and shims. Polyfills and shims consist of carefully crafted (in most cases) JavaScript, HTML and CSS code that help to provide the technology and functionality that you expect the browser to provide natively. In simple words, they provide support for missing features.

While both polyfills and shims try to offer the same thing, they go about it in slightly different ways and there are benefits to both. With polyfills, the code tries to replicate the real, standards-based API, making it easier to port your applications once a standard feature is included in a future version of a browser. This makes it easier to “future-proof” your code since theoretically, you shouldn’t have to change anything for the new feature to work.

A great example of a polyfill is Ryan Seddon’s H5F, which works to emulate the new HTML5 Forms functionality. Few browsers support this new feature, so the H5F polyfill will do feature detection to determine the browser’s level of HTML5 Forms support and emulate functionality if it’s not available.

Shims are also important because, in many cases, they offer features that you’re really interested in but no polyfill is available and you want enhanced features not available in the standard implementation. I always like to refer to Guillermo Rauch’s Socket.IO as a great example of an alternative to the WebSockets specification, especially since WebSockets is still a moving target.

The key thing is that Socket.IO has its own API and developers will have to update their code if they want to shift to WebSockets in the future.

Here’s an example of a shim in action. Suppose I want to use the new CSS3 border-radius property to create nice rounded corners for my DIVs. When a browser supports this new property, it will render my DIVs in this fashion:

Without broad browser support, though, the user will see a degraded version of my DIVs in a non-modern browser:

In most cases, this is perfectly fine. But when you absolutely want to provide a consistent experience, regardless of browser version, polyfills and shims do the trick. In this case, I’ll use Mike Alsup’s jQuery Corner plugin as my shim. Looking at the code below, jQuery is loaded and then using the Modernizr feature detection library, I check to see if border-radius is supported in my browser. If it’s not, then I dynamically load the plug-in and execute its functionality on the target DIVs:

The result is VERY similar to using the native CSS3 border-radius property and provides the rounded corner UI in non-modern browsers:

If you look closely at the corners, you’ll see that the native border-radius implementation provides smoother rendering than the plugin. But again, the point is that there is now a consistent experience for your users.

Treading lightly

Just because polyfills exist, it doesn’t mean that you should charge in blindly, loading up a bunch of libraries. There are very important considerations to take into account including performance and code maintenance.

It’s important to remember that by using a third library, especially one created by a sole developer, you may some day have to support the code yourself. Open-source projects are started and abandoned every day, so selecting a polyfills or shim requires a good amount of due diligence to ensure that you understand what the code is doing and that you can manage it in the event of abandonment.

In addition, let’s not forget all the performance tips gleaned from Steve Souders' outstanding investigations into client-side performance. Remember that the more external files you need to load, the more blocking actions you’ll have in your web application, causing performance to be negatively affected.

A solution to the previous problem is Alex Sexton’s fabulous conditional loader, YepNope. It’s an asynchronous resource loader that works to load specific scripts based on conditional logic. That way, you’re only loading the scripts you need and at the right moment.

The other great thing about YepNope.js is that Alex is working with the Modernizr team to include Modernizr’s awesome feature detection capabilities. This will make an incredibly powerful combination, providing robust and tested feature detection into a mature resource loader.

Here’s an example of YepNope in action. In the following code, the loader will test for the existence of the Geolocation API and if it does not exist, will load the polyfill “geolocation-polyfill.js”:

It’s incredible simple syntax that provides a tremendous amount of power and flexibility.

When can we use HTML5?

Well, the obvious answer is now, just with some caveats. We have to keep in mind that non-modern browsers aren’t going away any time soon and browser feature fragmentation may never go away completely. So while we wait for market share on browsers such as IE6 and Safari 3.x to go to zero, we can use polyfills and shims to give users a consistent experience across browser versions.

Note, though, that’s it is perfectly okay to offer users of non-modern browsers a degraded experience. In many cases, they won’t even be able to tell that they’re being served a subset of features and you save yourself the aggravation of trying to provide parity for all users.

But in those cases where you absolutely need to have the same experience, using polyfills and shims in a manageable way can help you bridge the gaps and provide the rich features your users want.