Using Modernizr to detect HTML5 features and provide fallbacks

by .

Modernizr logo

Modernizr is a JavaScript library that detects which HTML5 and CSS3 features your visitor’s browser supports. In detecting feature support, it allows developers to test for some of the new technologies and then provide fallbacks for browsers that do not support them. This is called feature detection and is much more efficient than browser sniffing.

Modernizr is very useful for detecting CSS3 support, but this article will focus on HTML5. The principles are essentially the same, though.

It’s important to note that Modernizr doesn’t “fill in the gaps” (i.e., polyfill) for you. It only detects whether something is supported. But you can use Modernizr as part of the polyfilling process.

Versions of IE8 and below do not recognise new-in-HTML5 elements by default, so you have to fix this with some JavaScript. You could possibly create each of these yourself with the below code, or you might use the HTML5 Shiv by @rem, which includes all the new elements.

Modernizr does all this for you, so you don’t need include the Shiv.

Getting Started

First, you need an HTML document:

<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
  <meta charset="utf-8">
  <title>Hello Modernizr</title>
  <script src="modernizr.js"></script>
</head>
<body>
</body>
</html>

You can see in the code above that you need a modernizr.js file. You have to build and download this yourself by choosing the features you want to detect. Do this by choosing the ‘Production’ option in Figure 1 and then ticking the necessary options in Figure 2. This helps keep the file size down by not detecting everything Modernizr is capable of. There is a development version (see Figure 1) that you can link to whilst developing your site, but before you put the site live, you should build the production file you need.

Modernizr download options
Figure 1: Modernizr download options
Modernizr build options
Figure 2: Modernizr configuration & build options

Also notice the second line of the HTML above: there is a no-js class on the <html> element. Modernizr first removes this and replaces it with a js class, which could be useful in your CSS. Along with the js class, it adds classes for all the features the browser supports and for the features it does not support, pre-fixing those with no-.

Here are two examples, one from Chrome 16 and one from IE9:

<html class="js flexbox canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths">
Figure 3: Modernizr’s feature detection in Chrome 16
<html class="js no-flexbox canvas canvastext no-webgl no-touch geolocation postmessage no-websqldatabase no-indexeddb hashchange no-history draganddrop no-websockets rgba hsla multiplebgs backgroundsize no-borderimage borderradius boxshadow no-textshadow opacity no-cssanimations no-csscolumns no-cssgradients no-cssreflections csstransforms no-csstransforms3d no-csstransitions fontface generatedcontent video audio localstorage sessionstorage no-webworkers no-applicationcache svg inlinesvg smil svgclippaths">
Figure 4: Modernizr’s feature detection in IE9

Do Some Detecting

Modernizr creates a global Modernizr JavaScript object, which allows us to query different properties of that object to perform feature detection by calling Modernizr.<featurename>. So to test for canvas support, you would write the following:

<script>
  if (Modernizr.canvas) {
    alert("This browser supports HTML5 canvas!");
  }
</script>

So try this page in a modern browser and there will be a nice message for you.

Because you’re all good developers and write unobtrusive, progressive JavaScript so that everyone can use your site, you also want to check if canvas is not supported. You have a couple of options here. You could use the above if statement along with an else statement, like so:

<script>
  if (Modernizr.canvas) {
    alert("This browser supports HTML5 canvas!");
  } else {
    alert("no canvas :(");
  }
</script>

Or if you want to test purely for no canvas support, you can negate the condition like this:

<script>
  if (!Modernizr.canvas) {
    alert("No canvas here");
  }
</script>

Feature Detection and Polyfilling with YepNope

In the examples above, you’ve seen the simplest way to detect a browser feature. What if you wanted to detect a feature and use a polyfill to make the browser perform better? You can do this with YepNope.

YepNope is a conditional loader, which means it will only load the scripts that are needed by the browser. And it’s built into Modernizr, so you don’t have to worry about downloading and linking to another JavaScript file.

So how do you use it?

Using canvas as an example again, you’ll generally want a fallback for non-supporting IE8 and below. The usual way of doing this would be to link to a JavaScript polyfill, such as FlashCanvas in your HTML:

<script src="http://flashcanvas.net/bin/flashcanvas.js"></script>

The problem with this approach is that all browsers will download this script. That’s unnecessary and should be avoided where possible. You could arguably wrap the <script> element in conditional comments, but if you can keep the files out of the markup altogether, then you should. You can do this using Modernizr.load(). Modernizr has YepNope built into it, so you can easily test for a feature and then supply a polyfill.

So let’s take a look.

You should note that .load() is not included in the development file by default. You need to include it and build it yourself.

The basic use of the .load() function allows you to test for a feature and ask whether it’s true (yep) or false (nope). In this example, Modernizr tests for canvas support, and if the feature doesn’t exist, then it loads FlashCanvas:

Modernizr.load({
  test: Modernizr.canvas,
  nope: 'http://flashcanvas.net/bin/flashcanvas.js'
});

So open up IE8, take a look at the network tab (Figure 5) in the developer tools. With the above code, you’ll see that it downloaded and initialised flashcanvas.js. Pretty nifty, yes?

Figure 5: FlashCanvas resources loaded in IE8

Here’s a more practical example that detects the support of <input type="date">. If it isn’t supported, it loads in two jQuery files and a CSS file to generate a styled date picker:

<script src="modernizr.js"></script>
<script>Modernizr.load({
  test: Modernizr.inputtypes.date,
  nope: ['http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js', 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.min.js', 'jquery-ui.css'],
  complete: function () {
    $('input[type=date]').datepicker({
      dateFormat: 'yy-mm-dd'
    }); 
  }
});
</script>

This test looks for <input type="date"> support. When it fails, it loads in the two external jQuery JavaScript files and a local CSS file. (Our tests suggest that the CSS file needs to be local.) Once it’s done that (i.e., on complete), it then calls the plugin for each <input type="date"> in the DOM. In most browsers, the jQuery will be loaded (Figure 6), but in Opera, the files aren’t loaded because it has a native calendar control (Figure 7):

jQuery date picker widget
Figure 6: jQuery date picker widget in Firefox
Opera date picker widget
Figure 7: Native date picker widget in Opera

The two figures below show the difference in which resources the browsers downloads:

Resources loaded in Firefox
Figure 8: Date picker resources loaded in Firefox
Resources loaded in Opera
Figure 9: Date picker resources loaded in Opera

Notice how the two jQuery JavaScript files are loaded twice. That’s a YepNope behaviour, first loading the resource and then executing it. So don’t worry, that’s normal.

You can do much more with YepNope. Here’s an example taken from the YepNope web site of all the possible properties, all of which are optional:

yepnope([{
    test : /* boolean(ish) - Something truthy that you want to test */,
    yep : /* array (of strings) | string - The things to load if test is true */,
    nope : /* array (of strings) | string - The things to load if test is false */,
    both : /* array (of strings) | string - Load everytime (sugar) */,
    load : /* array (of strings) | string - Load everytime (sugar) */,
    callback : /* function ( testResult, key ) | object { key : fn } */,
    complete : /* function */ }, /* ... */ ]);

Conclusion

Modernizr is a powerful feature detection library. It allows you to check whether a browser supports various features and, depending on what you want to achieve, lets you use a polyfill. This article looked at how to generate a Modernizr JavaScript file and examined two different ways of using Modernizr: directly using the Modernizr object (Modernizr.<featurename>) and then using the built-in YepNope.

So, what do you think? Let us know in the comments below!

24 Responses on the article “Using Modernizr to detect HTML5 features and provide fallbacks”

  • Ralph says:

    Is it also possible to serve JavaScript/jQuery fallbacks for certain CSS3 properties with Modernizr load?

    For instance I use the ‘background-size’ property set to ‘cover’ for some background images. For IE8 and below I do some UA sniffing and inject the background images as img tags (class=”background”) with jQuery into the DOM.

    In my CSS I do then:
    .no-backgroundsize .background {
    // width: 100%; position: absolute; z-index: -1; etc trick
    }

    But I can only do my fallback like this for the old IE and not for the other older browsers that don’t support the background-size property, so can I use Modernizr load for this instead of UA sniffing?

  • Justin Toon says:

    Thank you for this very clear and concise explanation of Modernizr! As a self-taught front-end developer trying to learn and keep up, articled like this are very helpful.

  • Tien Do says:

    Thanks, but since Modernizr.load is not available in Development version, how can I use YepNope in this case? I don’t want a custom version of Modernizr (that includes .load) because it is minified.

  • checkov says:

    Great article

    A couple of noob questions:

    You could arguably wrap the <script> element in conditional comments I thought conditional comments were only for IE?

    By Our tests suggest that the CSS file needs to be local do you mean on the same domain?

    Thanks

  • I’m used in all my projects web.

    Now, can’t I use Modernizr for my web app, extensions for browsers or app for devices mobile?

  • Markus says:

    Thank you for the good introduction!

  • Modernizr is a great tool, but we must not forget that any page that relies on Modernizr to work, will not work at all in Browsers where Javascript is not activated.

    It is of course possible to state that Javascript is so essential today, that we can completely disregard those that turn it off. It might even be correct, but I’m amazed that there is so little discussion about that important point nowadays.

    Has the web developer community already made the decision, that there is no need to think about users that don’t have Javascript activated? (It does seem CSS support is already treated that way.) Does anyone ever test their pages with Javascript turned off these days? Do we need to?

  • Ralph says:

    @Bertil Wennergren
    You will have to add a no-js class to the HTML tag and than Moderniizr detects if a browser supports javascript or not. If yes, it replaces the no-js with js. If not it stays no-js. With no-js you can create the fallback solutions for non javascript users/devices in your CSS. The user experience will be not that great, but the site is still usable and the content is still 100% accessible.

  • “but the site is still usable and the content is still 100% accessible”

    Not if you don’t actually test in e.g. MSIE6-8 with Javascript deactivated. How many actually do that? Sometimes new HTML5 elements will not result in a usable page in MSIE6 witout Modernizr or the Shiv.

  • Ralph says:

    “Not if you don’t actually test in e.g. MSIE6-8 with Javascript deactivated.”

    If the website stats indicates that I must create non-js fallbacks than of course I should also test my progress in the browsers with JavaScript turned off.

    I believe every web developer that’s serious in what he’s doing does that.

  • Vicky W says:

    Doesn’t this usage of Modernizr mean that our CSS needs to be written in the HTML file? .css files can’t have javascript…

  • Ray says:

    @Vicky W
    No javascript would need to be in the .css file.

    All the javascript is either on the HTML page itself or called from a .js file.

    If Modernizr detects something is not supported it uses the built-in YepNope script to conditionally call any supporting .js and/or .css files you may need to polyfill the browsers lack of support.

  • Those saying that what about users with JS disabled. Go try and use Facebook without it. I wish you good luck while you are at it btw.

  • Thanks for article , Im glad to see Chrome is now the most popular browser and IE9 is being rolled out on new systems. The days of IE7-IE8 will soon be a thing of the past but in the meantime this tool works great.

  • Nick says:

    Anyone who is browsing the web with a browser capable of javascript, yet with it turned off, is either a retard, or is using that computer for something that would end in them not visiting random websites…

    Every device nowadays supports javascript, so you can count on javascript being enabled for your site… or your site is for retards… either way…

  • JeffNunn says:

    The web runs on Javascript and has run on Javascript since the early 2000’s. If someone has their Javascript turned off, they have essentially turned off the web altogether. Might as well turn of CSS and HTML while you’re at it. I don’t waste my time testing for turned off JS.

    While testing for compatibility with SCREENREADERS is related, it is not the same thing as testing for people who have turned off JS

  • Alex says:

    Nice little article – great high level description of some of the really useful features of this tool – thanks.

  • Alastair says:

    I think at this point, anyone who deliberately turns off Javascript deserves exactly what they get. My belief is there is not enough tinfoil in the world for the hats of those who browse the internet with Javascript turned off and certainly those people aren’t visiting the websites I develop.

  • cwebba1 says:

    I’m VERY excited by this article, planning to try your example of loading JQuery within Modernizer after previous failed attempts.
    Please clarify about the “Modernizer is same as Yepnope” statement, which I see oft repeated. Specifically I want to know about your example of yepnope properties – I see the same switch of reference in every tutorial – can I use “modernizer” instead of “yepnope” with those property patterns in Modernizr?
    (Is “modernizr ([{ callback:” OK to use?)
    Are the two names the same thing within Modernizr? I cannot be sure whether you are saying to load the yepnope program or to use “yepnope” as part of the modernizr syntax? Please nail this down.
    Another thing that I cannot find is an example of combining two javascript polyfills within one “script.js” file. Mr. Doubting Thomas here wants to SEE how it looks. Every tutorial skips ahead to “too hard to do so use a Concatenator / minifier” and skips over this critical bit of information.
    The third big bit of info that I’m wondering about is about the path to polyfills to call within Modernizr. If I combine multiple polyfills into one scripts.js file, do I just call that one scripts.js file for every time I need a polyfill? Wouldn’t that load the entire scripts.js file multiple times? Or should I not combine/minify scripts when using Modernizr and just keep each one separate?

  • Nitin says:

    Thanks it was one of the best explainations

  • bouks says:

    Better solution for your example of detecting input type date and datepicker.

    Often you have already included jquery and jquery ui for other things in the website.
    In this case, your example will not work and in a capable browser you will have both native and jquery datepicker.

    The solution :


    if (!Modernizr.inputtypes.date) {
    //optional
    Modernizr.load(['/js/jquery.ui.datepicker-yourlangage.js']);

    $('input[type=date]').datepicker({
    dateFormat: 'yy-mm-dd',
    minDate: "-100Y",
    maxDate: "-18Y",
    changeMonth: true,
    changeYear: true
    });
    }

  • @html5Doctor,
    @bouks,

    I just set up a couple of demos to test the example code, and I ran into a problem with Modernizr 2.6.2 and the latest version of Chrome. The complete callback of Modernizr.load is called regardless of the test results. This causes an error when jQuery is not loaded, so I have two alternatives to the example you posted:


    Modernizr.load({
    test: Modernizr.inputtypes.date,
    nope: [
    'js/jquery.min.js',
    'js/jquery-ui.min.js'
    ],
    complete: function () {
    if (window.jQuery) {
    jQuery('input[type=date]').datepicker({
    dateFormat: 'yy-mm-dd'
    });
    }
    }
    });

    In this case, we simply add a test for the presence of jQuery to the complete callback.


    Modernizr.load({
    test: Modernizr.inputtypes.date,
    nope: [
    'js/jquery.min.js',
    'js/jquery-ui.min.js'
    ],
    callback: {
    'js/jquery-ui.min.js': function () {
    $('input[type=date]').datepicker({
    dateFormat: 'yy-mm-dd'
    });
    }
    }
    });

    In this case, I use the callback property with the “js/jquery-ui.min.js” key. Since “js/jquery-ui.min.js” is only loaded when the test fails, we don’t need the conditional.

    Finally, as bouks mentions, possibly you are certain that jQuery and jQuery UI are already on the page. In that case you don’t need the Modernizr loader a’tall, just the test:


    if (!Modernizr.inputtypes.date) {
    jQuery('input[type=date]').datepicker({
    dateFormat: 'yy-mm-dd'
    });
    }

    My demos are located at:
    http://jeffreybarke.net/demos/modernizr/date-picker-loader-1.html
    http://jeffreybarke.net/demos/modernizr/date-picker-loader-2.html
    http://jeffreybarke.net/demos/modernizr/date-picker-loader-3.html

  • That is handy for detecting html 5 features in the browser. great they included the shiv for old versions of Internet explorer also.

  • Adrián says:

    Hello, first sorry for my English, I’m working on a web application, the case problem is I have a date with the guy in the safari browser. I need to be in date format (dd-mm-yyyy), and tested with the example of your article but I can not move, so I ask for help if you can help me with this. Thanks in advance.

  • Leave a Reply to Markus

    Some HTML is ok

    You can use these tags:
    <a href="" title="">
    <abbr title="">
    <b>
    <blockquote cite="">
    <cite>
    <del datetime="">
    <em>
    <i>
    <q cite="">
    <strong>

    You can also use <code>, and remember to use &lt; and &gt; for brackets.