Zach’s ugly mug (his face) Zach Leatherman

Et tu, X-UA-Compatible?

February 06, 2011

Or, the story of how I learned that the X-UA-Compatible header/meta tag is NOT the same as the Internet Explorer 8 Compatibility View button.

Please note that the following information may be common knowledge, as this behavior is as described in the pre-requisite Microsoft documentation on the subject. However, I feel this behavior to be unintuitive and requiring more explicit communication.

Library developers live in a much different world from full stack developers. If you’re supplying code that will be used by others, you’re faced with a different set of priorities. As such, the library I manage provides certain user agent sniffing conveniences, namely classes to replace CSS Hacks (similar to the approach used in HTML Boilerplate to provide ie6 through ie9 classes on the <html> tag) and JavaScript booleans (similar to jQuery.browser). Much like jQuery, these conveniences remain for backwards compatibility.

The purpose of this post is in fact not to convince you whether or not User Agent Sniffing is a good tradeoff for performance (even though, in some cases I believe it is), but instead to provide further evidence that using the User Agent alone is an unreliable method to determine the rendering engine of the web browser, and must be complemented with other approaches. If you’re going to sniff, you have to sniff harder.

X-UA-Compatible Breaks navigator.userAgent

Microsoft defines two terms to help communicate how new versions of Internet Explorer render a page: Browser Mode and Document Mode. Browser Modes are determined prior to any request made to the server (and cannot be changed by web developers). If the user triggers an IE7 Browser Mode using the Compatibility View Button, a new request is made to the server. Document Modes are determined in the page response.

Compatibility View Button Flow

  1. User presses the Compatibility View button. This changes the Browser Mode.
  2. Request sent (since the Browser Mode has changed, the User-Agent is also changed to MSIE 7.0)
  3. Response from Server
  4. Document Mode determined

X-UA-Compatible Flow

  1. Internet Explorer uses the default Browser Mode (newest available in the browser)
  2. Request sent (Browser Mode determines User-Agent to send, probably MSIE 8.0 or MSIE 9.0)
  3. Response from Server. Content may include X-UA-Compatible <meta> tag and/or Response HTTP headers such as X-UA-Compatible).
  4. Document Mode determined, using X-UA-Compatible and the DocType

If a X-UA-Compatible header is sent back in the Compatibility View flow, it will take precedence but obviously will not change the request User-Agent HTTP header.

It’s important to note that since the request has already gone before the Document Mode is determined, the Document Mode has no bearing on the request User-Agent HTTP header. While Microsoft probably could have changed navigator.userAgent to be different than the request User-Agent HTTP header, I feel they made the correct decision is keeping the same value. navigator.userAgent remains the same value as the request User-Agent HTTP header in IE8, but Microsoft changed this behavior in IE9. In IE9, navigator.userAgent represents the document mode, not the request User-Agent header.

The User Agent isn’t the only thing being determined. The prerequisite Microsoft documentation states that the Browser Mode determines the User Agent, Default Document Mode, and Conditional Comments. This is not accurate. The Document Mode determines which Conditional Comments execute, not the Browser Mode.

Also note that using IE’s Conditional Compilation feature to return the version of JScript will be the same, independent of Document Mode.

var ie = /*@cc_on @_jscript_version @*/0;
// Always returns 5.8 in IE8, independent of Document Mode.

Example Code from Scott Jehl’s respond.js

Test Pages

Try the following tests in Internet Explorer 8 to test for yourself.

What Does This Mean?

If you use X-UA-Compatible to change the Document Mode/IE Trident rendering engine, any client side code relying on navigator.userAgent (such as jQuery.browser) or server side code relying on the User-Agent HTTP Header will be incorrect. Since we can’t rely on the User Agent, what can we use? Microsoft does provide a document.documentMode integer we could use, pretty easily. But, I think there is a better approach.

Consider a great piece of JavaScript written by James Padosley to find Internet Explorer version numbers using Conditional Comments. His code seemed to meld perfectly with the above realization that the Document Mode determines Conditional Comment execution. We can use this to fix jQuery.browser.

    // Place this script after jQuery, but before any code that uses jQuery.browser
    // Modified to only test for IE 6 , since jQuery only supports 6
    (function($)
    {
        if(!$.browser.msie) {
            return;
        }

        var v = 5,
            div = document.createElement('div'),
            all = div.getElementsByTagName('i');

        while (
            div.innerHTML = '',
            all[0]
        );

        $.browser.version = v;
    })(jQuery);

Now, of course, since we’re creating nodes there is a small performance penalty in using the above script over Regular Expressions and document.documentMode. I feel this to be worthwhile, given that I’ve been burned by Internet Explorer’s version number before. But next time, I reserve the right to choose performance over simplicity.


< Newer
The JavaScript Testing Challenge Winner
Older >
BigText Makes Text Big

Zach Leatherman IndieWeb Avatar for https://zachleat.com/is a builder for the web at IndieWeb Avatar for https://cloudcannon.com/CloudCannon. He is the creator and maintainer of IndieWeb Avatar for https://www.11ty.devEleventy (11ty), an award-winning open source site generator. At one point he became entirely too fixated on web fonts. He has given 78 talks in nine different countries at events like Beyond Tellerrand, Smashing Conference, Jamstack Conf, CSSConf, and The White House. Formerly part of Netlify, Filament Group, NEJS CONF, and NebraskaJS. Learn more about Zach »

3 Comments
  1. Aviv Ben-Yosef Disqus

    17 Oct 2011
    Just wanted to say thanks! I just had the same problem and your post helped me solve this and fix my browser detection.
  2. Roatin Marth Disqus

    05 Nov 2011
    Re "The Document Mode determines which Conditional Comments execute, not the Browser Mode."Nope, other way around. Test http://jsbin.com/uxahuf#noedit in IE9, in IE9 Browser mode: you get an alert("9") no matter what document mode you pick.
  3. Zach Leatherman Disqus

    06 Nov 2011
    Yes, Roatin, you would be right if you were strictly looking at manual modification of the Browser and Document Modes from the developer tools. It's good that you pointed that out. That mechanism represents the Microsoft documentation correctly. But this analysis is looking at real world scenarios that developers have access to from code (X-UA-Compatible), which appears to operate differently.Here's your test case using the meta tag:http://jsbin.com/uxahuf/2#n...In IE8: Note the "Setting X-UA-Compatible IE=7 using a Meta Tag (or IE=EmulateIE7)" test cases above. The user agent clearly denotes the Browser Mode (IE8), but the Document Mode is reported as IE7. In both test cases, only the IE7 conditional comment content is shown.IE9: In IE9 navigator.userAgent now matches to the document mode, not to the browser mode. However, the test cases still represent Browser Mode of IE9, Document Mode of IE7, and Conditional Comments report IE7 as well.
Shamelessly plug your related post

These are webmentions via the IndieWeb and webmention.io.

Sharing on social media?

This is what will show up when you share this post on Social Media:

How did you do this? I automated my Open Graph images. (Peer behind the curtain at the test page)