I’ve been reviewing bug submissions for the SWFObject project and was reminded of a big problem with SWFObject 2.2: the JavaScript technique it uses for detecting Internet Explorer does not work in Internet Explorer 9.
SWFObject 2.2 currently uses an IE detection technique proposed by Andrea Giammarchi in 2009:
var isIE = !+"v1";
Code language: JavaScript (javascript)
It’s a hack that relied on Internet Explorer’s unique handling of vertical spaces (v). Now that it has stopped working, I decided to take a quick look at what others are doing.
One of my favorite approaches is Dean Edwards’ “sniff” technique, which takes advantage of Microsoft’s conditional compilation.
var isIE = /*@cc_on!@*/!1;
Code language: PHP (php)
It’s 4 years old, but still works like a charm. The only problem is that some JavaScript compressors and optimizers (including YUI Compressor and Google Closure) have a hard time with the inline comment and strip it out. It makes the code difficult to maintain, because it requires editing post-compression.
One way to get around the compressor issue (as pointed out by a commenter on Dean Edwards’ post) is to wrap the conditional compilation statement in an eval()
function:
var isIE = eval("/*@cc_on!@*/!1");
Code language: JavaScript (javascript)
Since eval()
is evil, I won’t use this approach.
One of the most long-standing methods of detecting Internet Explorer is to examine the userAgent string:
var isIE = /msie/gi.test(navigator.userAgent);
Code language: JavaScript (javascript)
However, this isn’t foolproof, as most browsers allow you to change the userAgent string at will. For example, I changed Safari’s userAgent string to report itself as Internet Explorer 8, and /msie/gi.test(navigator.userAgent);
returned true
!
So far, the only test I’ve found that seems to fit the bill is the old navigator.appName
test:
var isIE = navigator.appName === 'Microsoft Internet Explorer';
Code language: JavaScript (javascript)
Is it perfect? Not a chance, but it has a lot of upside: it works in IE9, doesn’t get damaged when compressed, doesn’t rely on any hacky tricks (as fun as they might be), and isn’t affected by spoofed userAgent strings.
What are the Frameworks using?
jQuery uses userAgent sniffing, and includes a warning that it’s unreliable. MooTools uses a combination of properties from the navigator
object, including navigator.userAgent
and navigator.platform
. It’s interesting to note that MooTools previously used feature detection to infer the browser brand, but changes introduced by Firefox 3.6 prompted the MooTools team to switch to a userAgent-based detection method instead. (Nicholas Zakas wrote an interesting blog post about MooTools’ prior detection technique.)
Dustin Diaz’s Bowser detection script uses userAgent exclusively.
Why sniff in the first place?
Before you get down on me for even talking about sniffing, relax, I agree with you. I much prefer feature detection to browser sniffing; this is especially important with our quickly changing browser landscape: HTML5, web storage, geolocation, etc. (Modernizr is a great tool for modern feature detection.)
But Internet Explorer always makes us do things we’re not quite comfortable doing. Sometimes it’s not very easy to detect IE’s support for a given issue. For example, IE is notorious for not allowing JavaScript developers to set the name attribute of form elements via setAttribute; it requires including the name in the createElement invocation. Craziness!
So we carry on. I use feature detection for just about everything, but once in a while I need a global “are you Internet Explorer?”, and browser sniffing fits the bill.
Comments
Jesse wrote on June 29, 2011 at 2:22 pm:
Another option is documented at http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ where you conditionally apply an "ie" class to the HTML tag, and then check whether the HTML tag has that class with JavaScript.
philip wrote on June 29, 2011 at 2:35 pm:
@jesse that's an excellent approach, but unfortunately it's also not pure JavaScript, so it will only work in specific circumstances. In the case of a JavaScript library like SWFObject, jQuery or MooTools, the library can't depend on HTML-based conditional comments. I do like the concept, though; it's the most foolproof way to not only detect IE but get the exact version being used.
danjah wrote on July 27, 2011 at 5:00 pm:
Gidday Pipwerks – I felt obliged to try and add in some browser sniffing after reading, I found the following worked a treat – it isn't "elegant" but for IE it's pretty solid:
Am I IE?
<pre>
var p = document.createElement('p');
p.innerHTML = '<!–[if IE 9]>IE9<![endif]–>';
p.style.display = 'none';
document.body.appendChild(p);
if(p.innerHTML === 'IE9'){
alert('This is… IE9!');
}
</pre>I did it with YUI3 initially, in the same # of lines of code, but reduced it to good ole JS.
I'd also tried to avoid string testing, by dynamically creating script tags with a variable test inside – but then remembered that IE borks on that approach – maybe there's a way to do it.
but either way, creating an element with an inline conditional comment works as well as the comments themselves!
cheers,
d
danjah wrote on July 27, 2011 at 5:01 pm:
Doh – the important bit got stripped out when I submitted – but basically the innerHTML is set to be an IE conditional comment, all I put inside the comment was the string: "IE9", which I then test for.
– d
philip wrote on July 27, 2011 at 6:01 pm:
@danjah i fixed the post for you. thanks for the suggestion, it's an interesting approach.
Another option is documented at http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ where you conditionally apply an “ie” class to the HTML tag, and then check whether the HTML tag has that class with JavaScript.
@jesse that’s an excellent approach, but unfortunately it’s also not pure JavaScript, so it will only work in specific circumstances. In the case of a JavaScript library like SWFObject, jQuery or MooTools, the library can’t depend on HTML-based conditional comments. I do like the concept, though; it’s the most foolproof way to not only detect IE but get the exact version being used.
Gidday Pipwerks – I felt obliged to try and add in some browser sniffing after reading, I found the following worked a treat – it isn’t “elegant” but for IE it’s pretty solid:
Am I IE?
I did it with YUI3 initially, in the same # of lines of code, but reduced it to good ole JS.
I’d also tried to avoid string testing, by dynamically creating script tags with a variable test inside – but then remembered that IE borks on that approach – maybe there’s a way to do it.
but either way, creating an element with an inline conditional comment works as well as the comments themselves!
cheers,
d
Doh – the important bit got stripped out when I submitted – but basically the innerHTML is set to be an IE conditional comment, all I put inside the comment was the string: “IE9”, which I then test for.
– d
@danjah i fixed the post for you. thanks for the suggestion, it’s an interesting approach.