Yet another HTML5 fallback strategy for IE

If you’re using HTML5 elements then you’re probably also using a JavaScript shiv to help make it possible to style those elements in versions of Internet Explorer prior to IE9. But when JavaScript is disabled the accessibility of the content may be affected in these versions of IE. This is one way to provide a more accessible fallback.

The concept is to ensure that all modern browsers are served the default style sheet(s) and that people using older versions of IE only download them if JavaScript is enabled. When JavaScript is not enabled, people using those browsers can be served either no styles at all (as Yahoo! suggests for browsers receiving C-Grade support) or simple fallback styles.

Client-side method: conditional comments

Doing this on the client-side comes at the cost of having to litter your code with proprietary conditional comments. First, it’s necessary to comment out the default style sheet(s) from versions of IE earlier than IE9. All other browsers will be able to read the file(s).

<!--[if ! lt IE 9]><!-->
<link rel="stylesheet" href="/css/default.css">
<!--<![endif]-->

For earlier versions of IE, an HTML5 shiv is included and the necessary link elements are created and added to the DOM using JavaScript. This means that when JavaScript is not enabled in IE7 or IE8 the style sheet will not be present, resulting in an unstyled HTML page. In this example, IE6 won’t be served CSS at all.

<!--[if (IE 7)|(IE 8)]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script>
   (function() {
      var link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = "/css/default.css";
      document.getElementsByTagName("head")[0].appendChild(link);
   })();
</script>
<![endif]-->

To support multiple style sheets, an array and for loop can be used.

<!--[if (IE 7)|(IE 8)]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script>
   (function() {
      var css = [
         '/css/default.css',
         '/css/section.css',
         '/css/custom.css'
      ],
      i = 0,
      link = document.createElement('link'),
      head = document.getElementsByTagName('head')[0],
      tmp;
      link.rel = 'stylesheet';

      for(; i < css.length; i++){
         tmp = link.cloneNode(true);
         tmp.href = css[i];
         head.appendChild(tmp);
      }
   })();
</script>
<![endif]-->

Thanks to Remy Sharp and Mathias Bynens for helping me to improve this script. Fork it.

Rather than serving unstyled content, it may be preferable to provide some simple fallback styles. This can be done by linking to a separate style sheet wrapped in noscript tags. In this example, IE6 will always use these legacy styles while IE7 and IE8 will do so only when JavaScript is disabled.

<!--[if lt IE 9]>
<noscript>
   <link rel="stylesheet" href="/css/legacy.css">
</noscript>
<![endif]-->

You may wish to use a generic style sheet, such as “Universal IE6 CSS”, or spend a few minutes crafting your own and ensuring that the typography and colours approximate those in the default style sheet.

The complete example code is as follows:

<!--[if ! lt IE 9]><!-->
<link rel="stylesheet" href="/css/default.css">
<!--<![endif]-->

<!--[if (IE 7)|(IE 8)]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script>
   (function() {
      var link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = "/css/default.css";
      document.getElementsByTagName("head")[0].appendChild(link);
   })();
</script>
<![endif]-->

<!--[if lt IE 9]>
<noscript>
   <link rel="stylesheet" href="/css/legacy.css">
</noscript>
<![endif]-->

Server-side method: user-agent string detection

The drawbacks of current client-side approaches to IE fallbacks is that they are IE-specific, make extensive use of conditional comments, and have to use JavaScript to create or rewrite link elements. This blog makes use of an alternative approach: server-side user-agent detection. It was inspired by Yahoo!’s Graded Browser Support strategy – created by Nate Koechley – which recommends that all CSS and JavaScript is withheld from legacy browsers (not limited to IE).

The source code in the head of this blog changes when viewed in modern browsers, IE8, and legacy browsers that are incapable of styling HTML5 elements (e.g. Firefox 2) or lack adequate CSS2.1 support (e.g. IE7).

Browsers are assumed to be capable; there is no need to update the script every time a new browser is released. Only when a browser is deemed to be severely incapable is it added to a “blacklist” and served simple styles to ensure that the accessibility of the content is maintained. This is the method I prefer, although it does require more time upfront.

Reply on Twitter Retweet on Twitter Favorite on Twitter

13 comments

#

David Hund says…

Thanks for the nice writeup Nicolas, always good to have options.

I would be interested in seeing your server-side user-agent detection script. Do you have it up anywhere?

#

Nicolas says…

@David Hund: The PHP code for the UA detection and HTML output isn’t up anywhere yet. It was put together from existing pieces of code. Once it’s been tidied up and made more flexible I hope to put it on Github so others can fork it if they want.

#

David Owens says…

This looks cool. I’m working on a new site at the moment where the CSS is split across several files. I then concatenate and minify for production.

I’m thinking I could serve typography.css as the default “legacy” file.

I just remembered conditional comments can severely slow down the experience for IE users. Turns out conditional comments block CSS downloads.

You can fix the problem by including an empty conditional comment before any of the other CSS. <!–[if IE]><![endif]–>

#

Nicolas says…

@David Owens: The IE conditional comments issue isn’t a problem in this case. The code used in the client-side method is like that found in the “Browser-sniffing comments” section of Stoyan Stefanov’s article.

#

David Owens says…

@Nicolas – so it is. I’d missed that part. Cheers for pointing it out.

#

Elco Klingen says…

A good writeup.

I’ve discovered another method; it can be easily applied to an existing website without using javascript, conditional comments or server-side replacements. It also has no negative consequences for modern browsers. A fallback strategy for IE is no longer needed, at least for the styling bit of HTML5 anyway. One downside: it’ll break validation unless it’s served as XHTML5, but then, perhaps validation is more of a guideline anyway.

http://www.debeterevormgever.nl/html5-ie-without-javascript/

#

Dwight Vietzke says…

Hi all,

A similar strategy that I ran across was using code like this:

// Only run these for IE < version 9 (I hate IE!)
/*@cc_on
    /*@if (@_jscript_version < 9)

        msos.ie_fix.html5();
        msos.ie_fix.html5_print();

    /*@end
@*/

// Only run these for IE < version 7
/*@cc_on
    /*@if (@_jscript_version < 7)
    
        msos.ie_fix.flicker();

    /*@end
@*/

..in your external javascript file instead, where ‘msos.ie_fix.html5′ is just my Dojo toolkit style call to ‘html5shiv’ code. I need to look at the stylesheet stuff above, but that might also be able to be loaded dynamically based on the above javascript.

The point is, everything is in one external javascript file that only gets called for IE.

#

Nicolas says…

@Elco: The downside isn’t validation issues; the downside is marking up your document like that, escaping colons in CSS files, using the XML declaration, having the server send application/xhtml+xml to modern browsers and text/html to Internet Explorer, and ensuring that there are no errors at all in the document…simply to cater for people using Firefox 2 or IE<9 with JavaScript disabled. It’s impractical and involves making a lot of changes if it is to be applied to an existing HTML5 site.

#

Elco Klingen says…

@Nicolas: If the downside isn’t validation issues, then you can simply only use the namespace part. Transforming the markup to XHTML is only for validation purposes. Your HTML5 won’t validate completely when using the namespace, but it will work. It’s a hack, I admit, but I’ve seen no other solution for clients who have Internet Explorer and disabled Javascript.

#

Elco Klingen says…

I’ve made a simple example:

http://www.debeterevormgever.nl/html5-ie-without-javascript/?page=example_html&lang=en

I doesn’t validate, but it does work. No reformatting to XHTML needed if you either don’t want to or it’s too impractical. But the choice is there; if you absolutely need it to validate for some reason (difficult boss, perhaps ;-)), that’s also possible:

http://www.debeterevormgever.nl/html5-ie-without-javascript/?page=example_xhtml&lang=en

#

Nicolas says…

@Elco: Thanks for sharing what you’ve done. Personally, I’m not going to compromise my markup in that way and take to escaping characters in the style sheet simply to cater for the few people using IE<9 with JavaScript disabled. IMO, the aim shouldn’t be to provide an identical experience but to provide a fallback that makes the content more accessible. But it’s definitely interesting stuff.

#

Elco Klingen says…

@Nicolas: You’re welcome. And I agree, a good fallback is usually enough. But then there are always clients…

Better to have a method like this and never need to use it, then needing it and not knowing it, right?

#

Rick Harris says…

I was trying to get this method to work with a Drupal project, and I was killing myself trying to get the for loop working exactly right.

Then it hit me, I already have a PHP variable containing all the <link rel="stylesheet"/> tags. So I decided to try document.write(''); instead of the for loop, where the str_replace() just escapes any line breaks for Javascript. The same thing could obviously be done in static HTML with some manual cut and paste action, and it’s at least no more redundant than redefining the files in an array.

This has made for faster page loads for me when testing in IE with multiple stylesheets defined. Also, this method will respect your media attributes if you use those on stylesheets. Finally, using document.write puts your stylesheets right where you want them to be in your <head> rather than appending them to the end.

I know document.write is sometimes looked down upon, but I’ve never engaged in that debate. So someone tell me if what I’m doing is silly.

Comments are now closed.