The dangers of browser detects

I'm already regretting the publication of my Browser Detect 2.0. From one of the comments I learned that there's a new script making the rounds of blogs, a script that neatly highlights the dangers of using browser detects, but that's been received with glad cries by otherwise sensible sites.

Combine the release of this script with my release, and it might seem to the unaware web developer that browser detects are back in fashion. It's necessary to repeat why browser detects are dangerous, unprofessional and usually badly written, even though those facts have been general knowledge since at least 1998.

The script

Rafael Lima's script adds classes to HTML elements based on the results of an old-style (ie. purely navigator.userAgent-based) browser detect. The purpose is to allow web developers to add CSS rules for one browser by using these classes.

37 Signals and Ajaxian enthousiastically covered it; in fact, the script was written as a result of the 37 Signals blog entry. Unfortunately neither 37 Signals nor Ajaxian seem to have considered the problems.

Incorrectly written

The first problem is that it's incorrectly written. Consider this screenshot:

Screenshot of the browser detect script in Safari disguised as Internet Explorer. The box is yellow, but it should be black.

The browser is obviously Safari, but the box is yellow; the colour that indicates Internet Explorer. Why does the script misfire? Because I disguised Safari as Internet Explorer (Debug -> User Agent -> pick your browser). The script doesn't penetrate this disguise.

This is the first, and clearest, danger. Browser vendors have been avoiding browser detects since 1994 by changing their navigator.userAgent value to what the browser detects script expect. Does the script expect "MSIE"? Then the user agent string contains "MSIE", even though the browser is not in fact Internet Explorer.

So the script is unreliable: you can never be certain that your carefully crafted Safari styles will actually be implemented when the browser is Safari.

My script checks navigator.vendor instead; and right now this property contains "Apple" in Safari, regardless of the chosen value of navigator.userAgent. This state of affairs is unlikely to continue indefinitely, though, because as soon as people start using navigator.vendor-based detects to deny entrance to their sites, browser vendors will make sure that the value of this property, too, can be changed to match the one that the detection scripts expect.

So even if the script would switch to navigator.vendor-based detection wherever possible, it's still not 100% future-proof (neither is mine, by the way).

Bad solution

Even if the script were perfectly reliable, however, it still would be the wrong solution to the stated problem. Although not quite as bad as CSS hacks, applying special styles by means of a browser detect still assumes that the CSS bugs they're supposed to work around will never be solved.

Suppose you discover a CSS bug in, say, Opera 9, and use the script to work around it:

div.special {
	padding-top: 30px;
}

html.opera div.special {
	padding-top: 20px;
}

Initially this works fine. However, let's say the bug is fixed in Opera 10. Now, just as with CSS hacks, a special style is applied that is not necessary any more and the CSS will be broken in Opera 10. There are two ways of solving this problem:

  1. Remove the html.opera div.special rule.
  2. Add a version detect to the script and use something like html.opera9 div.special.

Solution 1 requires you to constantly update your site whenever a new browser appears. Besides, if you remove the special rule, your CSS will work correctly in Opera 10, but not in Opera 9. Therefore it doesn't really solve the problem. When writing CSS, you must make sure that it will continue to work in future browsers, instead of being forced to recheck all your old sites in new browsers.

At first sight the second solution seems preferable. (Lima's script doesn't yet contain a version detect, but it could be easily upgraded to include it.) The html.opera9 div.special rule does not work in Opera 10, and that's what we want.

However, now suppose that the bug is not solved in Opera 10. Now you have to add an extra selector html.opera10 div.special, and this once again means that you have to maintain all sites you've ever created.

Using a browser detect means that you have to constanly maintain the site that contains it: professionalism requires you to keep an eye on new browsers, check all your sites in them, and update the special rules for or against this or that browser that you've added. This quickly leads to a maintenance hell—one of your own making.

The right solution

Actually, I feel that creating special style sheets is only necessary for Explorer 6 and lower. The other browsers, and that includes the Explorer 7 beta, support CSS well enough to allow you to write one single style sheet to serve all of them.

Separating IE 6 and lower from all other browsers is simple:

<link rel="stylesheet" href="styles.css" />
<!--[if lt IE 7]>
	<link rel="stylesheet" href="ie6.css" />
<![endif]-->

The conditional comment makes sure that the second style sheet is read only by Explorer 6 and lower, and your problem is solved.

That is not to say that all other browsers support CSS perfectly; everybody knows they don't. Nonetheless, I found time and again that it's quite possible to work around these bugs by simply ignoring the buggy declaration in favour of non-buggy ones. True, this solution requires quite a bit of CSS and browser knowledge, but acquiring such knowledge is part and parcel of being a professional web developer. If you're really stuck you can always ask the nice people at CSS-D for help; many members have encountered the same problem and will be able to help you.

If you think you need a browser detect to solve a CSS problem, you're wrong. Always.

This is the blog of Peter-Paul Koch, mobile platform strategist, consultant, and trainer. You can also follow him on Twitter.
Atom RSS

I’m speaking at the following conferences:

(Data from Lanyrd)

Categories:

Monthlies:

Comments

Comments are closed.

1 Posted by Eric Meyer on 3 August 2006 | Permalink

Not that I disagree with your overall point, but isn't a conditional comment just another kind of browser detect?

2 Posted by Colin Ramsay on 3 August 2006 | Permalink

Eric, I think you're right, but I think that point is mitigated by this one:

"Actually, I feel that creating special style sheets is only necessary for Explorer 6 and lower."

We shouldn't need to be using browser detects to work with modern browsers versions. Come IE7, we should have good versions of each browser and can work with them on pretty much the same level.

But for "broken" implementations such as IE6, I think another approach must be taken and an exception can be made.

3 Posted by ppk on 3 August 2006 | Permalink

Not really, in my opinion. It's an official, documented, though non-standard, feature of the IE browser that's explicitly meant to identify a specific IE version.

There are two things that could go wrong:
1) MS drops support (not likely; not when the IE Blog officially recommends them as The Way to separate IE6- from all other browsers)
2) Other browsers start claiming they're IE (not likely, either; why would any other browser want an IE 6 style sheet?)

In contrast, any browser may invent a new value of navigator.userAgent (or any other navigator property).

I feel conditional comments are safe--for a few years.

4 Posted by pawel on 3 August 2006 | Permalink

Some time ago I used a combination of special class name for IE and conditional comments:
<!--[if lte IE 6]>
<script type="text/javascript">
document.documentElement.className='ie6'
</script>
<![endif]-->

Then I was able to use
.ie6 someselector { }

Now I prefere additional stylesheet for IE<7 (works when JS is disabled) and I use documentElement.className = 'js' without conditional comments so I can easily write CSS rules for JS enabled and disabled version in one stylesheet, like:
.js span.tooltip { display:none }

In my personal opinion now there are two kinds of browsers: IEs and almost-standards-compliant, so any browser detection other than conditional comments doesn't seem to be necessary.

5 Posted by Masklinn on 3 August 2006 | Permalink

> Not that I disagree with your overall point, but isn't a conditional
> comment just another kind of browser detect?

While i'd consider it another kind of browser detect as well, it's a "safe" one compared to sniffing userAgent or vendor: it's part of a public, stable Microsoft API, and it won't change. If you're targetting IE lt 7, then no matter what the user does with his browser (barring completely hacking the exe itself) the styles will only be used for IE6 and under.

And I really doubt other browsers will implement conditional comments, or at least implement conditional comments and identify themselves as MSIE (that would be dead stupid anyway, even more so since CCs are mostly used to patch MSIE's flaws)

6 Posted by Su on 3 August 2006 | Permalink

So, it's still just another form of browser detect, but my completely ignored my suggestion of vendor extensions:
http://37signals.com/svn/archives2/browser_selectors_in_css.php#c34041
is documented, standard and doesn't require external technology. Thoughts?
Should obviously still be discouraged(and is in the spec), but it seems the cleanest potential solution.

7 Posted by Klaus Hartl on 3 August 2006 | Permalink

Thank you for expressing far better what I wanted to say in my comment here:
http://37signals.com/svn/archives2/browser_selectors_in_css.php#c34106

Oh, and regarding IE 7 not needing any hacks: this is not true unfortunately. The most infamous bugs were fixed, but in my last project I made the experience, that it still has all these annoying bugs which have to do with having layout or not.
I ended up using Conditional Comments like lte IE 7 and in that style sheet I used either advanced CSS selectors or the underscore hack to target the two versions (most of the time such seperation wasn't necessary because both browsers needed layout triggered or something).

8 Posted by Johan on 3 August 2006 | Permalink

I found this script to serve different styles for a targeted browser (no browser detect but object detect)

http://www.hedgerwow.com/360/dhtml/css_modern_hack.html

9 Posted by Steve Clay on 4 August 2006 | Permalink

Yep, I know you're right.. I just hate *hate* having to use conditional comments (MS stay out of my markup!) and I think separating the hack values from the "real" CSS values makes a maintenance nightmare. The solution to the 2nd problem would be a server-side or design-time script that strips the IE6 rules out of style.css and auto-generates ie6.css for us.

10 Posted by TarquinWJ on 5 August 2006 | Permalink

@Johan,

You have totally missed the point of what an object detect is. That script uses a browser sniffer. Ok, so it does not rely on the navigator properties much, but it is still a sniffer. An object detect is where you detect a property *that you are about to use*, not where you detect one thing (an object) and assume another (a browser name, or an unrelated object).

//This is a sniffer (and it is wrong)
if(document.all)
ie=true;

//This is a sniffer (and it is wrong)
if(document.all)
canDoFilters=true;

//This is a proper object detect
if(document.all)
myElement=document.all['bar'];

The script sucks anyway. It relies on browsers not implementing the same bugs or features as each other later. For example, if Safari implemented document.getBoxObjectFor (which it would be perfectly within its rights to do), the script would think it was Gecko. When Mozilla suite implements Array.map, it will think it is Firefox. Stay away from that sort of script - it will cause serious future problems.

11 Posted by Binny V A on 8 August 2006 | Permalink

Condition comments may be a form of browser detect - but at least it is better than using CSS hacks or JavaScript hacks to make site 'work'.
I use(and prefer) conditional comments to the other alternatives.
By the way, what is your opinion about Dean Edward's IE7
http://dean.edwards.name/IE7/

12 Posted by Emrah Baskaya on 12 August 2006 | Permalink

Some bugs are detectable, if the bug's result is one that can be measured. In one of my scripts, I was detecting a firefox bug that caused an element to have wrong widths, and I was feeding it an alternative function that worked around the problem; this was before Firefox 1.5, and in Firefox 1.5, the problem was gone, and voila, the new Firefox was instantly using the "normal" routine.

13 Posted by Thorsten on 23 August 2006 | Permalink

The bugs of the browsers making me crazy. I donĀ“t try to find a special solution for a special (old) browser, but I allways solve the problems by find a solution that works for most of the important browsers. When you start supporting browser version, you have to spend a lot of time, especially when you think about future.