Lesson 9

Feature Support & Polyfills

Building a website can be both extremely rewarding and frustrating. Common frustrations arise from trying to get a website to look and perform the same in every browser. All front end developers have shared this frustration at one point or another.

Truth be told, websites do not need to look or perform the same in every browser. Exactly how close a website works in each browser is up to you and your level of comfort for a given website. If a website receives under half a percent of traffic from Internet Explorer 6 it might make sense to drop support for it. If that half a percent is still contributing to thousands of dollars in sales, support may be mandatory. Determine what is acceptable for a given website and work from there.

There are a handful of common practices to get websites to perform adequately in all browsers, some of which have already been covered within this guide. When incorporating CSS3 properties, fallbacks are recommend to support older browsers. Other techniques include shivs and polyfills. Generally speaking, shivs and polyfills are small JavaScript plugins that add support for a requested set of features not natively supported by a specific browser.

HTML5 Shiv

Perhaps the most popular shiv, and one you may have likely used already, is the HTML5 Shiv. The HTML5 Shiv was created by Remy Sharp to provide the ability to use HTML5 elements within versions of Internet Explorer 8 and below. The HTML5 Shiv not only creates support for HTML5 elements but also allows them to be properly styled with CSS.

The shiv should be downloaded from Google, where Remy maintains the latest version, then hosted on your server. For the best performance, reference the shiv JavaScript file within the head of the document, after any stylesheet references. Additionally, you want to reference the shiv inside of a conditional comment, making sure that the file is only loaded within versions of Internet Explorer 8 and below.

In this case the conditional comment looks like <!--[if lt IE 9]>...<![endif]-->.

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

The Difference Between a Shiv & a Shim

Chances are you may have heard of both the HTML5 Shiv and HTML5 Shim, and wondered what the difference, if any, may be. Oddly enough, there is no difference between the HTML5 Shiv and HTML5 Shim. The two words are often used interchangeably and are commonly transposed.

Additionally, once the new HTML5 elements are created using the shiv, any block level elements need to be identified and updated using the display: block; declaration.

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
  display: block;
}

Lastly, Internet Explorer 8 and 9 do not correctly define styles for a few HTML5 inline-block level elements. As before, these styles will need to be explicitly stated. After which, all versions of Internet Explorer should be good to go using any new HTML5 elements.

audio,
canvas,
video {
  display: inline-block;
}

Detecting Browser Features

Referencing the HTML5 Shiv works well with a conditional comment because the intention is to specifically target browsers that don’t support new HTML5 features and elements. Additionally, there is a way to to provide support for specific HTML5 and CSS3 features, regardless of which browser is being used.

Feature detection, as provided by Modernizr, provides a way to write conditional CSS and JavaScript based on whether or not a browser supports a specific feature. For example, if a browser supports rounded corners Modernizr will add the class of borderradius to the html element. If the browser doesn’t support rounded corners, Modernizr will add the class of no-borderradius to the html element.

Loading Modernizr

To get feature detection with Modernizr up and running, simply visit their download page and customize what features you are looking to detect, or use the out of the box development version. Most commonly the development version is the way to go, however, if you are only looking for support for one feature, a custom build would be more appropriate. Once downloaded, upload the JavaScript file on your server and reference it within the head of your HTML document, below any referenced style sheets.

It is worth noting that Modernizr may be configured to include the HTML5 Shiv, in which case the shiv doesn’t need to be referenced on top of Modernizr.

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

Conditionally Applying CSS Styles

Once Modernizr is up and running CSS styles may be conditionally applied based on the features a given browser supports. Modernizr has detection for the majority of the CSS3 properties and values, all of which can be found in the Modernizr documentation.

One item to weigh out is if feature detection is necessary for certain styles. For example, using an RGBa color value may easily be supported with a fallback hexadecimal value without the use of feature detection. When deciding to use feature detection, it is important to keep styles organized and performance in mind. Avoid duplicating any code or making additional HTTP requests when possible.

button {
  color: #fff;
  font-weight: bold;
  padding: 10px 20px;
  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.6);
}

/* With CSS Gradient Styles */

.cssgradients button {
  background: linear-gradient(#687587, #404853);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3);
  border-radius: 4px;
}
.cssgradients button:hover {
  background: linear-gradient(#5a6675, #333942);
}
.cssgradients button:active {
  background: #333942;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.75);
}

/* Without CSS Gradient Styles */

.no-cssgradients button {
  background: transparent url("button.png") 0 0 no-repeat;
}
.no-cssgradients button:hover {
  background-position: 0 -36px;
}
.no-cssgradients button:active {
  background-position: 0 -72px;
}

Feature Detection Demo

In the demonstration above, the button inherits some default styles. However, specific styles are only applied based on whether or not CSS3 gradient background are supported. In this case, rounded corners and box shadows are also included within the conditional styles. Those browsers that support gradients get a gradient background, rounded corners, and a box shadow. Those browsers that do not receive an image with all of these styles included within the image. With this code none of the styles are being over written and an HTTP request is only made when necessary.

When working with CSS3 feature detection it is hard to know what the styles look like in browsers that do not support specific CSS3 features. Fortunately, there is a bookmarklet called deCSS3 which disables any CSS3 features. Doing so allows you to see what a website would look like without CSS3, and if your conditional styles are working. To get a quick idea of what an individual browser supports, visit haz.io within that specific browser.

Conditionally Loading Files

On top of conditionally loading styles, Modernizr also provides a way to conditionally load files based on feature support. With this, JavaScript polyfills may be loaded based on feature detection. Modenizer, with the help of yepnope.js, can be configured to include the Modernizr.load resource loader.

The Modernizr.load resource loader tests for most HTML5 and CSS3 features, which may be identified within a test condition. Should the condition pass, any files referenced within the yep block are loaded. Should the condition fail, any files referenced within the nope block are loaded. Both the yep and nope blocks are optional, so long as at least one of them is identified. Making these optional allows you to only load files based upon whether a condition is specifically on or off.

One amazing feature of the Modernizr.load resource loader is that it can load any external files, not only JavaScript but also CSS.

Modernizr.load({
  test: Modernizr.localstorage,
  yep : 'js/storage.js',
  nope: 'js/storage-polyfill.js'
});

Multiple files may be loaded efficiently using multiple conditions by applying the operators && and ||. The && operator corresponds with the logical AND while the || operator corresponds with the logical OR. In the following example, multiple conditions are used to load different files, only loading the necessary files once. If support is unavailable for either the autofocus input attribute or CSS animations, then the jQuery library is loaded. If support is available for each individual feature, then individual polyfills are loaded.

Modernizr.load([
  {
    test:  Modernizr.input.autofocus || Modernizr.cssanimations,
    nope: 'js/jquery.js'
  },
  {
    test: Modernizr.input.autofocus,
    nope: 'js/autofocus.js'
  },
  {
    test: Modernizr.cssanimations,
    nope: 'js/animations.js'
  }
]);

Chris Coyier wrote a forms progressive enhancement tutorial using Modernizr and polyfills, which leverages a handful of different operators for the most proficient code.

Conditionally Loading Based on Media Queries

One interesting condition Modernizr can test against is media queries. Doing so provides the ability to only load files based on different media query conditions. Not loading unnecessary files can be extremely beneficial for performance.

Modernizr.load({
  test:  Modernizr.mq('screen and (min-width: 640px)'),
  nope: [
    'js/jquery.js',
    'js/tabs.js'
  ]
});

Above, Modernizr looks to detect screens above 640 pixels wide, primarily desktops, and then loads files and functions based off of this condition. It is important to note that this condition is tested only once, when the page loads, and that is it. Should a user resize the page, this condition will not be retested. Should this condition need to be retested, additional JavaScript would need to be included.

Conditionally Running Scripts

On top of conditionally loading files, Modernizr also helps to conditionally run JavaScript. All of the HTML5 and CSS3 features available to be tested within the Modernizr.load resource loader may also be tested within scripts. For example, it may be worth disabling tooltips on mobile devices due to not having hover capabilities, and instead showing the tooltip in plain text on the screen. The script for calling these tooltips could be wrapped in a Modernizr condition, preventing the script from loading on smaller screens.

$(document).ready(function() {
  if (Modernizr.mq('screen and (max-width: 800px)')) {
    $('.size').text('small');
  }
});

Conditionally Running Scripts Demo

Your screen width is large.

Above is a basic example of how JavaScript can be executed based on a condition established by Modernizr. Upon loading the page, if the screen is above 800 pixels wide nothing happens. However, if the screen is below 800 pixels wide, upon being loaded, the word ‘small’ will be swapped for ‘large’ based off of the executed JavaScript.

HTML5 & CSS3 Polyfills

Currently there are polyfills for nearly all of the different HTML5 and CSS3 features. The team over at Modernizr has put together quite an exhaustive list of polyfills. These polyfills can be dropped in as needed.

The same people behind Modernizr have also put together a list of all of the new HTML5 and CSS3 features, including instructions on how to use them responsibly. Understand, not all of these features need polyfills. Quite a few of them can be used outright or with the use of a fallback.

Cross Browser Testing

Perhaps the most dreaded part of web design and development is cross browser testing, making sure a website works well in all browsers. Generally speaking the more modern browsers, Chrome, Firefox, and Safari, all perform pretty well. The largest pitfalls live within Internet Explorer, and testing different versions of Internet Explorer can be difficult.

There are a handful of services out there that do help with cross browser testing, some are interactive while others are not. Being able to interact with a browser, rather than seeing a rendered screenshot, is far more helpful for debugging code. One of the best ways to boot up multiple versions of Internet Explorer is by using multiple virtual machines, each with a different version of Internet Explorer.

VirtualBox
Fig. 10.01 VirtualBox running on Mac OS X with Internet Explorer versions 6 through 9.

Microsoft provides a handful of VirtualPCs that can be used solely for testing. Setting all of these up can be a herculean task. Fortunately, Greg Thornton has built an automated installer for all of these virtual machines. The installation takes a while to download all of the different virtual machines, and requires a decent amount of disk space. Prepare adequately by only installing the necessary virtual machines and by clearing up the necessary disk space ahead of time. Depending on how often the virtual machines are used, it may be worth installing them on an external hard drive.

Internet Explorer versions 8 and above have built-in development tools, unfortunately versions 7 and below do not. The web inspector and all of the other debugging tools we’ve grown to love are not readily available within Internet Explorer 7 and below. There is, however, a Firebug Lite bookmarklet that provides an in browser inspector that is tremendously helpful.

VirtualBox
Fig. 10.02 Internet Explorer 7 running inside of a virtual machine with the Firebug Lite bookmarklet open for debugging.

Resources & Links

Complete! Any questions?

Please feel free to contact me on Twitter if you have any questions. Also, if you enjoyed this lesson please share it with your friends.

A Beginner’s Guide to HTML & CSS

A Beginner's Guide to HTML & CSS

A Beginner’s Guide to HTML & CSS is a simple & comprehensive guide dedicated to helping beginners learn HTML & CSS, working through all common components.

View the Beginner’s Guide to HTML & CSS