IE8 and X-UA-Compatible, Part Deux: Solutions

Disclaimer upfront: any opinions expressed in this post are the sole opinion of myself and do not necessarily reflect the opinion of any of my employers, past and/or present.

In part one of this article, I described the situation with IE8 and the X-UA-Compatible header that the browser uses to decide what rendering engine to use. By far the most important takeaway from that piece is that detecting a browser by sniffing its UserAgent string is a terrible practice. It was already an unreliable method of doing so, but now, with IE8, it is fundamentally broken and you simply cannot rely on it to ever accurately present you with what rendering engine you’re dealing with. And when we’re detecting for browsers, most of the time it’s the rendering engine we really care about.

Let’s look at the two primary solutions to dealing with the disparity between rendering engines: the first is more ideological, the second more pragmatic. Both are acceptable ways of doing web development; if you’re doing neither, then you probably need to work on that. But that’s my personal opinion.

Build sites the Right Way™

If you want to know what the right thing to do is, read no further than this section. The absolute best way to build and maintain a website is to completely remove your dependency on user agent sniffing—not just for IE, but for browsers in general. UA sniffing is an unreliable and dangerous practice and it is the primary reason the Web is so broken today, so stop sniffing the User Agent string and basing your code off of that. There are more reliable metrics to use; for JavaScript, the most prevalent one is feature detection. There is a great resource on feature detection that goes into incredible detail, and is an absolute must-read, but in a nutshell: feature detection is an approach wherein you first detect whether the browser supports the use of certain functions, before you actually use those functions. Practicing this method also has the very valuable benefit of making your code far more robust and forward-compatible.

As for CSS, there is no similar thing to feature detection[1]. There is, however, the nature of the language itself: CSS offers many different solutions for any given problem, and I can tell you from experience that there is almost always one that will work across browsers. Keep in mind when developing and testing your CSS that your first choice of layout/style approach may not work as well in some browsers, but that doesn’t preclude a different solution from working better. Over time, and with enough experience, you’ll learn to instinctively reach for the solutions that work well across all browsers, reducing if not eliminating your need for hacks.

The Good Alternative

The Right Way is obviously a rather ideological approach, but that doesn’t mean it isn’t the absolutely best way to do web development. However, real world constraints such as time and resources make it a less practical, sometimes not even possible solution for the website at hand. Fortunately, there is a way to detect for Internet Explorer’s various rendering engines reliably, and I mean absolutely 100% reliable. The reason for that is because of the so-called Version Vector.

The Version Vector is an IE-only mechanic that powers the more widely known IE-specific thing calledConditional Comments, simple HTML comments that IE parses differently from any other browser. Throughout all the various possible combinations of UserAgent, implementation of X-UA-Compatible and blacklisted domain or not, the one and only thing that accurately matches what rendering engine you’re dealing with is the IE Version Vector. No matter the UA, no matter what you set for X-UA-Compatible (if you even set it at all), the Version Vector will tell you what rendering engine you’re dealing with.

Conditional Comments in Internet Explorer exist as both HTML and as JavaScript implementations, but ironically, for IE8 they only have mutually exclusive detection patterns. What I mean by that is, you can’t use only one or the other to get the information you need. So, that’s why I created this small bit of code that will, guaranteed, tell you what rendering engine in Internet Explorer you’re dealing with. JavaScript libraries should take notice:

var __IE__ = false;
   @if ( @_jscript_version >= 5.7 )
      __IE__ = true;
   @elif ( @_jscript_version == 5.6 )
      __IE__ = 6;
      __IE__ = 1;
if ( __IE__ === true) {
   var elem = document.createElement('div');
   elem.innerHTML = '<!--[if IE 7]><div class="ie7"></div><![endif]--><!--[if IE 8]><div class="ie8"></div><![endif]-->';
   __IE__ = parseInt(elem.firstChild.className.substring(2), 0);
   elem = null;

After this code executes, any non-IE browser (regardless of any meddling with the UserAgent string) will only have a single variable, __IE__, which is false. But in any Internet Explorer browser, the value of__IE__ will now be guaranteed to match the rendering engine being used. If the browser is IE 5.5 or older, it will be set to 1 — this is, in my belief, a good enough differentiator because we really shouldn’t be worrying about anything older than IE 6 anymore at this point. Come on, people.

A quick run-through of that code might be educational, so let’s look at the most interesting lines:

Line 2: this is how a JavaScript IE Conditional Comment starts. All non-IE browsers will ignore everything until line 11.

Line 3: the IE-specific variable @_jscript_version maps to the browser chrome. IE5.5 = 5.5, IE6 = 5.6, IE7 = 5.7 and IE8 = 5.8, regardless of what rendering engine or UserAgent string is in use.

Line 11: this IF clause will only evaluate for IE 7 and 8. It is at this point that we need to do the HTML Conditional Comment check to see what rendering engine we’re dealing with. To do so, we create an element in node-space and then, the crucial line:

Line 13: we set the innerHTML value of that element to contain two Conditional Comments with a div inside each. IE will still correctly parse these Conditional Comments, which brings us to the final, really important line…

Line 14: we look at the first child inside the element in node-space. It will either have a className of “ie7” or “ie8” and this will depend solely and reliably on the rendering engine that’s being used. We then strip away the “ie” part, turn the number into an integer type (instead of a string) and voilá, the variable __IE__now contains the version number of the rendering engine.

Using the above code, you will be able to detect any version of IE and get the version of the rendering engine being used, giving you an easy way to build your code on top of it without any UserAgent sniffing and without any risk of engine-to-browser mismatch.

Dealing with the browser landscape today can, at times, be difficult and cumbersome, but the more you adopt the best practices in web development the easier it becomes.

  1. Yet. I have something related to this that’s merely waiting for approval and will hopefully go live soon.

If you liked this, you should follow me on Twitter!