Conditional Browser Feature Testing with Modernizr

There is nothing quite so sad, to a developer, to have a new feature or solved an issue, and, in the full bloom of paternal pride, find out that it doesn’t work for a client. Hopefully this doesn’t happen during a live presentation ( … you don’t have Browser X installed!? .. no problem I’ll just use what you have install … and ๐Ÿ™ #FAIL ), or at your clients site (or to their customers site ).

Sometimes this is because of dinosaur browsers (you know who you are), but it is also a reality of how fast the HTML5 is moving and that feature implemented across browsers (or at least those with non trivial install numbers) is uneven. Sometimes you want to use a feature which is on the HTML5 roadmap but not in all the browsers.

HTML Test is my favorite way to see what the HTML5 landscape looks like, as well as Can I Use, and StatCounter Global statistic’s for the broswer numbers. Also worth mentioning is a Microsoft tool that scans for common coding problems (it’s open source so you could also host your own version ) and Google’s Page Speed Insights.

Given that browser sniffing is a very bad thing, what is a developer to do? Detect features instead of browsers! This is where Modernizr comes to the rescue, a JavaScript library that detects HTML5 and CSS3 features in the userโ€™s browser.

After loading the modernizr.js you can do something like this to detect the WebGl is supported:

1
2
3
4
5
if (Modernizr.webgl) {  
    /* do something clever */  
} else {  
    /* do something else because the browser doesn't support the WebGL feature  */  
}

Which works nicely but Modernizr also has an asynchronous conditional resource loader (for CSS and Javascript) so you only load only the assets that the you need.

In the above case (for WebGl) you could support your WebGL feature by having a fall back this way:

1
2
3
4
5
Modernizr.load({
  test: Modernizr.webgl,
  yep : 'three.js', /* JavaScript 3D Library */
  nope: 'jebgl.js' /*  JebGL uses a fallback Java applet to emulate the WebGL canvas if needed */
});

If you only need the fallback (say for Round Corners via the CSS3 border-radius property) you can just do the ‘nope’ :

1
2
3
4
Modernizr.load({
  test: Modernizr.borderradius,
  nope: 'corner.js'
});

Of course, in this case you really want to load the JavaScript and apply it to the elements with certain classes or id’s with a callback like this :

1
2
3
4
5
6
7
8
9
Modernizr.load({
  test: Modernizr.borderradius,
  nope: [ "corner.js"],
  callback: {
      "corner.js": function () {
        $('.big-btn').corner('22px');
        $('.smal.btn').corner('20px');
      }}
});

Naturally you wrap up multiple polyfill scripts loads in one script, by giving the modernizr loader an array of strings and objects :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Modernizr.load([
  {
    test : Modernizr.fontface && Modernizr.canvas && Modernizr.cssgradients,
    nope : ['presentational-polyfill.js', 'presentational.css']
  },
 {
  test: Modernizr.input.placeholder,
  nope: [ "placeholder.js"],
  callback: {
      "placeholder.js": function () {
        $('#customer input').placeholder();
    }}
  },
  {
  test: Modernizr.borderradius,
  nope: [ "corner.js"],
  callback: {
      "corner.js": function () {
         $('.big-btn').corner('22px');
        $('.smal.btn').corner('20px');
      }}
  }
]);

And with polyfills for nearly every HTML5 feature that Modernizr detect, we can support dinosaur browsers, future features, and be happy! ๐Ÿ™‚

Leave a Reply