Feature detection is not browser detection
Browser detection has been a hot-button topic in web development for a long time. This battle pre-dates JavaScript browser detection by a couple of years and begins with the introduction of Netscape Navigator, the first truly popular and widely-used web browser. Netscape Navigator 2.0 was so far beyond any of the other available web browsers that web sites began looking for its specific user-agent string before returning any useful content. This forced other browser vendors, notably Microsoft, to include things in their user-agent string to get around this form of browser detection. A very hairy situation, indeed.
Feature detection
Since that time, web developers repeatedly rant that browser detection, specifically user-agent sniffing, is a horrible practice that should never be considered. The argument is that the code isn’t “future proof” and will have to be changed when newer browsers appear. The preferred approach, the chorus echoes, is feature detection. So instead of doing something like this:
if (navigator.userAgent.indexOf("MSIE 7") > -1){
//do something
}
You should do something like this:
if(document.all){
//do something
}
There is a distinction between these two approaches. The first is testing for a specific browser by name and version while the second is testing for a specific feature/capability. So user-agent sniffing results in knowing the exact browser and version being used (or at least, the one being reported by the browser) while feature detection determines if a given object or method is available. Note that these are two completely different results.
Because feature detection doesn’t rely on knowledge of which browser is being used, only on which features are available, it is trivial to ensure support in new browsers. For instance, when the DOM was young, not all browsers supported getElementById(), and so there was a lot of code that looked like this:
if(document.getElementById){ //DOM
element = document.getElementById(id);
} else if (document.all) { //IE
element = document.all[id];
} else if (document.layers){ //Netscape < 6
element = document.layers[id];
}
This is a good and appropriate use of feature detection because the code tests for a feature and then, if it’s there, uses it. The best part about this code is that as other browsers began implementing getElementById(), the code didn’t have to change; support for the new browsers was baked-in using feature detection.
The mixup
Somewhere along the lines, a lot of web developers grew confused about the distinction between the two approaches. Code started being written similar to this:
//AVOID!!!
if (document.all) { //IE
id = document.uniqueID;
} else {
id = Math.random();
}
The problem with this code is that a test for document.all is used as an implicit check for IE. Once knowing that the browser is IE, the assumption is that it’s safe to use document.uniqueID, which is IE-specific. However, all you tested was whether or not document.all is present, not whether the browser is IE. Just because document.all is present doesn’t mean that document.uniqueID is also available. There’s a false implication that can cause the code to break.
As a clearer statement of this problem, people started replacing code like this:
var isIE = navigator.userAgent.indexOf("MSIE") > -1;
With code like this:
var isIE = !!document.all;
Making this change indicates a misunderstanding of “don’t use user-agent sniffing.” Instead of looking for a particular browser, you’re looking for a feature and then trying to infer that it’s a specific browser, which is just as bad. This is called feature-based browser detection and is a very bad practice.
Somewhere along the line, developers realized that document.all was not, in fact, the best way to determine if a browser was Internet Explorer. Then you started seeing code such as this:
var isIE = !!document.all && document.uniqueID;
This approach falls into the “too clever” category of programming. You’re trying too hard to identify something by describing an increasing number of identifying aspects. What’s worse, there’s nothing preventing other browsers from implementing the same capabilities, which will ultimately make this code return unreliable results.
If you think such code isn’t being used widely, think again. The following snippet comes from MooTools 1.1.2 (note, the current version is 1.1.4, so this is from an older version):
//from MooTools 1.1.2
if (window.ActiveXObject) window.ie = window[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true;
else if (document.childNodes && !document.all && !navigator.taintEnabled) window.webkit = window[window.xpath ? 'webkit420' : 'webkit419'] = true;
else if (document.getBoxObjectFor != null || window.mozInnerScreenX != null) window.gecko = true;
Note how the code tries to determine which browser is being used based on feature detection. I can point out any number of problems with this, aside from philosophical, but the most glaring is that window.ie will report IE 8 as IE 7. Big problem.
Why doesn’t this work?
To understand why feature-based browser detection doesn’t work, you need only look back to high school math class, where logic statements are typically taught as part of geometry. Logic statements are made up of a hypothesis (p) and a conclusion (q) in the form “if p then q”. You can try altering the statement form to determine truths. There are three ways to alter the statement:
- Converse: if q then p
- Inverse: if not p then not q
- Contrapositive: if not q then not p
There are two important relationships among the various forms of the statement. If the original statement is true, then the contrapositive is also true. For example, if the original statement was “If it’s a car, then it has wheels” (which is true) then the contrapositive, “if it doesn’t have wheels then it’s not a car,” is also true.
The second relationship is between the converse and the inverse, so if one is true then the other must also be true. This logically makes sense because the relationship between converse and inverse is the same as between original and contrapositive.
Perhaps more important than these two relationships are the relationships that don’t exist. If the original statement is true, then there is no guarantee that the converse is true. This is where feature-based browser detection falls apart. Consider the true statement, “if it’s Internet Explorer, then document.all is implemented.” The contrapositive, “if document.all is not implemented, then it’s not Internet Explorer” is also true. The converse, “if document.all is implemented, then it’s Internet Explorer” is not strictly true (for example, Opera implements it). Feature-based detection assumes that the converse is always true when, in fact, there is no such relationship.
Adding more parts to the conclusion doesn’t help either. Consider once again the statement, “if it’s a car, then it’s has wheels.” The converse is obviously false, “if it has wheels, then it’s a car”. You could try making it more precise: “if it’s a car, then it has wheels and requires fuel.” Check the converse: “if it has wheels and requires fuel, then it’s a car.” Also not true because an airplane fits that description. So try again: “if it’s a car, then it has wheels, requires fuel, and uses two axles.” Once again, the converse isn’t going to be true.
The problem is fundamental to human language: it’s very hard to use a collection of singular aspects to define the whole. We have the word “car” because it implies a lot of aspects that we’d otherwise have to list to identify that thing in which you drive to work. Trying to identify a browser by naming more and more features is the exact same problem. You’ll get close, but it will never be a reliable categorization.
The fallout
MooTools backed themselves, and their users, into a corner by opting for feature-based browser detection. Mozilla has warned since Firefox 3 that the getBoxObjectFor() method was deprecated and would be removed in a future release. Since MooTools relies on this method to determine if the browser is Gecko-based, Mozilla’s removal of this method in the upcoming Firefox 3.6 release means that anyone running older versions of MooTools may have their code impacted. This prompted MooTools to issue a call to upgrade to the most recent version, which has the issue “fixed.” The explanation:
We have overhauled our browser detection to be based on the user agent string. This has become the standard practice among JavaScript libraries because of potential issues as Firefox 3.6 demonstrates. As browsers grow closer together, looking at “features” to separate them will become more difficult and risky. From this point forward, browser detection will only be used where it would be impossible not to, in order to give the consistent experience across browsers that one would expect from a world-class JavaScript framework.
Curiously, a quick look at MooTools 1.2.4 still shows feature-based browser detection using getBoxObjectFor():
//from MooTools 1.2.4
var Browser = $merge({
Engine: {name: 'unknown', version: 0},
Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
Plugins: {},
Engines: {
presto: function(){
return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
},
trident: function(){
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
},
webkit: function(){
return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
},
gecko: function(){
return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
}
}
}, Browser || {});
The usage of getBoxObjectFor() is slightly different. In effect, the approach has changed from using the converse to using the contrapositive. The problem with this change is that you can only positively not identify the browser. And once again, testing for a newly-removed method doesn’t really help.
What to do?
Feature-based browser detection is a very bad practice that should be avoided at all costs. Straight feature detection is a best practice, and in almost every case, is exactly what you’ll need. Typically, you just need to know if a feature is implemented before using it. Don’t try to infer relationships between features because you’ll end up with false positives or false negatives.
I won’t go so far as to say never use browser detection based on user-agent sniffing, because I do believe there are valid use cases. I don’t believe, however, that there are a lot of valid use cases. If you’re thinking about user-agent sniffing, keep this in mind: the only safe way to do so is to target a specific version of a specific browser. Trying to detect a range of browser versions is dangerous, fragile, and likely to break if the upper bound of the range is the most recent version of the browser. It’s also advisable to target a specific version that is not the most recent version. Why? Because you want to identify differences, and the easiest way to do that is to look backwards towards previous versions rather than trying to look forward at non-existent future versions. This also serves to protect your code from the future. The goal should always be to write code that won’t break when an unknown browser begins running it.
Note: if you’re considering user-agent sniffing, I wouldn’t recommend worrying about user-agent spoofs. You should always honor exactly what the browser is reporting as a user-agent. My approach has always been that if you tell me you’re Firefox, I expect that you act like Firefox. If the browser identifies itself as Firefox and doesn’t act like Firefox, that’s not your fault. There’s no point in trying to second-guess the reported user-agent string.
So the recommendation is to always use feature detection whenever possible. If it’s not possible, then fallback to user-agent sniffing browser detection. Never, ever use feature-based browser detection because you’ll be stuck with code that isn’t maintainable and will constantly require updating and changing as browsers continue to evolve.
Apologies
I really didn’t mean to pick on MooTools when I first started writing this post. It just happens to present a really good learning opportunity for other developers. The MooTools developers are smart folks who I’m sure are continuing to work to improve their library and actively support their large user base. We all go through a similar learning curve, and we can all learn from one another.
Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of Yahoo!, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
You can leave a response, or trackback from your own site.




15 Comments
Very good article and a solid explanation of the issues.
However, I’m going to have to disagree with you on the point that feature-based browser detection is bad. I’ve more often heard this called “browser inference” (nodding to your mention of “infer” in the article) and there are valid cases where a straight feature-detect will not suit. I’ll agree it’s definitely not preferable, but to say that it’s a bad practice is to ignore the cases where it’s the only viable option.
For instance, there’s no way to feature-detect if a particular flavor of CSS opacity is supported or not, since there’s no way in JavaScript to tell if the opacity was rendered or not.
Another example, which is what I ran into recently, is the quirky behavior that various browsers exhibit with regards to dynamic script loading. For example, Firefox and Opera will maintain execution order of a set of a dynamically inserted (and thus parallel loaded) script tags. The rest will not. This is critical behavior that a script loader (like LABjs) needs to know. And it’s impossible to feature detect for this. Another example? Whether the browser supports <WBR> or ­ (for word breaking).
So, questions like these must come down to browser detection, unfortunately. And more specifically, browser detection (user-agent sniffing) versus browser inference (feature-based browser detection).
I am definitely in the camp of user-agent sniffing being a bad thing. To really do so with any hope of being reliable, it’s a hopelessly complicated mess of quirky variations and regex voodoo. I’m sure you’ve seen the huge complex libraries like browsercap and such that attempted to be the complete answer to sniffing the agent.
Other than just complexity, here’s why I think UA sniffing is bad: not all times that user-agents are spoofed is actually the user’s fault or in their control. For instance, IIRC, I believe the mobile SkyFire browser spoofs the UA. Also, there are a number of common FF plugins/extensions which do so for various reasons other than to just mess up JS devs. For one reason or another, it may simply tweak the UA (not spoof to an entirely different browser) in such a way that those brittle pattern matches break. You know how many UA sniffs are not even tolerant of different case in UA’s? Lots! And lastly, as you mentioned, UA’s are *not* future proof, especially for browser families, which is sometimes necessary for the use cases I’m talking about.
Basically, I don’t think functionality should be based on something that a user can control (or worse yet, can’t control but something else controls it for them). This is just asking for problems. And btw, THOSE types of bugs out in the field are WAY harder to debug because there’s so many possible and devious ways they can crop up.
So, if UA sniffing is out, and a feature detection is impossible, the lesser evil (IMHO) would be browser inference. Now, there’s certainly BAD inference (like “document.all”) and that should be avoided. But not all browser inferences are logically flawed.
For instance, in LABjs, I use the following two inferences to find and identify Opera and Gecko (FF) browsers, for the reasons mentioned above:
is_opera = window.opera && Object.toString.call(window.opera) == “[object Opera]“;
is_gecko = (function(o) { o[o] = o+”"; return o[o] != o+”"; })(new String(”__count__”));
The opera inference uses the fact that Opera defines a special global called “window.opera”… not only does it check for that, but to guard against someone creating a fake “window.opera”, it checks if it’s of the special type “object Opera”… which cannot be faked. I consider that to be a fairly reliable inference for Opera.
The Gecko inference is a little more tricky, but luckily it’s a quirk that’s been around for a LONG time and probably is not going to change (for various reasons). It uses the fact that an object’s special “__count__” property cannot be changed/overwritten (it’s read-only). This one is a little weaker in that it’s *possible* some other browser may implement something like that some day… but it’s likely to be safe for awhile.
I’m not claiming the inferences are perfect, just that they are pretty good when in a pinch (if done correctly), and are certainly easier/more reliable than UA sniffing. Those two simple lines of code are simpler and smaller than “reliable” UA sniffs of the same purpose.
Bottom line: I think “browser detection” (and by extension, “browser inference”) gets a bad rap because it’s often done poorly. But that doesn’t mean there aren’t ways to do it more reliably. And sometimes, that’s just what your code needs.
Kyle Simpson on December 29th, 2009 at 3:57 pm
I can’t agree that browser inference is easier or more reliable than UA sniffing, specifically for the reasons I give in “Why doesn’t this work?” above. You’re relying on something that isn’t necessarily a defining characteristic to be exactly that. Everyone who pushes browser inference thinks that they’ve found something that is canonical, but as in the case of
getBoxObjectFor, it’s an assumption that can backfire and break.I’ll echo again my position: you should believe what the user-agent tells you because it’s job is to inform you how to interact with the browser. If the browser tells me to interact with it like I would Firefox, then that’s what I’m going to do. Why? Because it is the browser’s job to do so. If it lies for some reason, I assume that it’s doing so for a good reason.
I’d also caution that user-agent sniffing isn’t as fragile as you make it out to be. Any program, when written poorly and without the proper amount of insight, is bound to fail. Many user-agent sniffs do fall into that category but that doesn’t mean it can’t be done properly (for instance, I show a safe way to do so in my book).
In my experience, browser inference tends to lead to worse outcomes than user-agent sniffing, which is why I can’t recommend it.
Nicholas C. Zakas on December 29th, 2009 at 5:32 pm
You don’t think that window.opera is a defining characteristic of Opera? I think that’s an entirely different kind of thing than an *assumption* about a generic and deprecatable function like getBoxObjectFor() or document.all. If Opera decides to ditch window.opera at some point down the road, I think there will be some profound repercussions beyond browser inference snippets.
What I think we *should have* is a definitive read-only property (mimicing the example of window.opera in fact) that each browser exposes to JavaScript so the page can detect what browser it’s working with definitively. And it shouldn’t be complicated (like minute differences between versions), and it shouldn’t be brittle (like case-sensitive string parsing/regex’s in UA sniffing), and it shouldn’t be overwritable (intentionally or accidentally).
I think we should be pushing for some sort of simple standard that the browsers could agree on for this. Clearly, JS frameworks have tried to do that for us, but as you mentioned, they are often going to fail at it.
If the firefox browser masquerades as safari for some reason, you’re right, you probably should respect that, and the user should get whatever they get. But if there’s something as simple as a UA addition (like the Chrome-Frame plugin does), or a case-change to the UA as I’m aware has happened by some plugins, and either unintentionally breaks UA sniffing, then it proves that UA sniffing is just too brittle to be reliable.
Kyle Simpson on December 29th, 2009 at 9:38 pm
window.operamay be the sole exception. It’s hard for me to argue that it doesn’t describe the browser conclusively at this moment. You won’t get a similar admission about__count__, though.In regards to your last paragraph, I don’t believe that just because something can break that it means it’s brittle. There is always a situation where one or another technique will break. I actually think the user-agent serves it’s purpose pretty well and so using it in an appropriate manner is still far better than browser inference. Let’s just theorize about what would happen in LABjs if you used UA sniffing instead of browser inference.
For Opera, you might misidentify the browser due to automatic UA changing (the most likely scenario). Opera most frequently misidentifies itself as Firefox, so it would end up in the Firefox branch and behave the exact same way. Since it’s unlikely that Opera or Firefox would change their script loading/blocking behavior, this actually works just fine. If Opera misidentifies itself as another browser, then it may somewhat effect performance, but the page still works. In that case, it’s Opera’s fault that the user isn’t getting the best experience (as this is why they say they change the UA string).
It’s less likely to misidentify Firefox as something else. And again, worst case scenario is that scripts don’t download in parallel (which I’m assuming is the default case). If your site is properly constructed for script loading, as I’ve mentioned before, then this shouldn’t make a huge difference.
Trying to second-guess what the browser tells you is really straddling a thin line, and I just don’t think it’s worth the trouble.
Nicholas C. Zakas on December 30th, 2009 at 1:08 pm
Well written article. Especially like the fact that you are one of the people who find that there are cases where one needs a real browser (or better: engine) detection.
I am not sure about the user agent parsing though. If there is a unfakeable, pretty stable way to find out a browser using the engine features I would prefer that. Both, the user agent string and the features may change in future, so I think that both require the same amount of work to keep them up-to-date.
One thing which seems to be missing in the whole document is that you may need browser detection when building client-optimized scripts e.g. not just removing white space, comments, etc. but also remove all dead code for alternative clients. In qooxdoo for example we suggest to use the variant system for most client detection stuff. It does only a basic detection for the four engines: webkit, gecko, presto and trident. Using these variants reduces the application size by 10-20% depending on the application. To use feature detection might be possible in a few cases, but would negatively impact these size optimized builds. I think GWT using a comparable approach. Just to say use object detection whereever possible and client-detection whenever required is maybe easy, but not the best solution in all use cases.
BTW: I prefer this way to detect gecko based engines. Somewhat comparable to the Opera way (unfakeable as well):
gecko = window.controllers && Object.prototype.toString.call(window.controllers) == “[object XULControllers]“
Sebastian Werner on December 30th, 2009 at 1:52 pm
I can’t enter the debate, my compliments for the very detailed informations and distinctions you made, I would just suggest a testing environment to test which technique really works.
If the javascript code loads in SpiderMonkey then we are safe to “assume” the code will work everywhere.
In my cases the best approach has become feature/capability testing (also a bit more code/ing).
Diego Perini on December 30th, 2009 at 2:37 pm
@Nicholas — but precisley because window.opera represents a definitive characteristic, shouldn’t that be the model for what we push for the other browsers to implement? I agree that inferences are sub-optimal… I just don’t agree that the user-agent is the answer.
Kyle Simpson on December 30th, 2009 at 3:11 pm
The problem with pushing for definitive characteristics, like window.opera, is the fact that it’s up to the browser maker to implement those characteristics. It essentially becomes part of the BOM where there’s no set standard. Even if it was standard, there’s no guarantee browser makers would implement, much less follow, the standard. Besides, the information we need to determine the browser is already presented to us with the user-agent string.
The user-agent string exists for the sole reason of identifying browsers - it’s our best resource for browser detection. Does it really matter that it can, and does, get spoofed? What percentage of people browse with a spoofed user-agent string? Someone purposefully changing the characteristics of the browser shouldn’t be our concern. That’s like modding an XBox and toasting it. It’s not Microsoft’s concern that we ruined our hardware when attempting to change it. Why should we be concerned with people purposely spoofing the user-agent string?
Jeremy McPeak on December 30th, 2009 at 10:01 pm
I don’t think there’s any value to be gained by asking browser to implement definitive characteristics. Why? Because we already asked for this, and they gave us the user-agent string.
At work, I always encourage people to look at the amount of time they spend on a feature versus the impact that it has. If you look at the percentage of users who will arrive at a site with a spoofed user-agent string, your concerns essentially come down to less than 1% of all users (it’s worth noting, less than 1% also don’t have JavaScript enabled).
For the sake of argument, let’s assume that user-agent sniffing fails to accurately identify the browser in around 0.5% of visitors. The most likely culprit is someone using Opera since that’s the only major browser that automatically tries to misidentify itself, so let’s assume 0.4% are Opera users and 0.1% are not. Opera goes through great pains to ensure compatibility when it spoofs the user-agent string, i.e., when it identifies itself as IE,
document.alland other IE-specific features magically appear. I wouldn’t worry about these users because of this.That leaves 0.1% of users for whom we have no idea why they’re returning the wrong user-agent string. It could be a misconfiguration (I’ve seen this happen with IE) or perhaps a developer intentionally changing the user-agent string. The first instance is rare and I’ve only ever seen it happen twice. The second is more likely, but in that case, I really don’t care if the developer is mucking around. If he or she is smart enough to do that, then he or she is also smart enough to realize that it may affect pages. If we place these two groups into the 0.1%, I’d go for 0.08% developers and 0.02% misconfiguration.
So at the end of the day, you end up spending a lot of time worrying about 0.02% of users. Then, you need to determine the impact to these users. Will improper user-agent detection severely impact them (they can’t use the site at all) or barely have any impact (rounded corners aren’t there). When done properly, user-agent sniffing should never result in a severe impact to a misidentified user agent. To use LABjs as an example: does it severely negatively impact the user if files aren’t being downloaded in parallel? No, especially if there’s only two or three files being loaded (and of course, if you’re already following proper coding techniques).
Asking browsers to help us correctly identify the 0.02% of users that are the real problem seems like a fruitless endeavor.
Nicholas C. Zakas on December 31st, 2009 at 2:27 pm
Just to clarify something asserted in this thread:
If for some reason a non-FF/Opera browser (IE, Chrome, Safari, etc etc etc) were identified as FF or Opera, it would definitely cause LABjs to break down completely. You’d have nasty race conditions with your scripts executing in undefinable order, because LABjs couldn’t (and wouldn’t) do anything to try and ensure that they executed in proper order.
If on the other hand, a FF/Opera browser were misidentified as IE (or something else), loading would probably ALSO fail, but for a different reason. FF/Opera don’t support the preloading “cache” trick that the other browsers do, which means that any scripts being loaded cross-domain simply would not be loaded at all.
In either case, the page would likely mostly or completely break. Script loading (like LABjs) is a corner case, I know, but it’s still a valid use-case to discuss in the overall scheme of things.
So, at least for LABjs (and I’d really be willing to argue, most scripts/frameworks which actually go to the trouble to browser detect — not feature detect), identifying the browser correctly is pretty important. I don’t buy the game of small percentages, because the assumption is that one site’s traffic (like Yahoo for example) is representative of the whole, which it is not always. Again, inference and implication can bite us.
—————-
Now, if we look at the user-agent string detection method… and we assume for a moment that I *did* agree in principle that “spoofing” (or even unintentional “tweaking”) of the UA was not a concern we cared that much about: It is still true the code to do “proper and robust” (not just lazy, assumptive) UA sniffing is non-trivially longer and more complicated than the small browser-inference snippets I’m suggesting. There’s lots of crazy corner cases to take into account, and you have admitted it’s hard to properly serve the use case of detecting whole browser families, which is what I needed it for in the first place.
Certainly, this is a judgement call as to whether bigger (and possibly slower, given the regexes and string munging) code is a fair tradeoff for some theoretically slight increase in the “reliabilty” of the test… but I believe you’re just trading one kind of “unreliability” for another kind of “unreliability”. So, at best, I’d argue the two methods are roughly equally “unreliable”.
Browser inference code is smaller (and possibly minutely faster) that UA sniffing. So in a game of ties, IMHO, browser inference “wins”.
In any case, happy new year 2010 and thanks for this engaging discussion!
Kyle Simpson on January 1st, 2010 at 12:26 pm
Please remember that in 99.9999999999% of cases, you don’t want to be doing browser detection, either; if you think you need to be doing browser detection, most likely what you really need to be doing is rendering engine detection.
That is, you care about Gecko and a major version, WebKit and a major version, and so forth. If you’re looking for “Firefox” in the user-agent string, then you’re already breaking your site in every other Gecko browser, as well as in nightly builds of Firefox (not just development builds, but nightly builds for security and stability updates, e.g. what will be released as Firefox 3.5.7 in a few days), making it harder for your users to use your site and harder for browser developers and QA to make sure they haven’t broken things while fixing other bugs.
Of course, if you’re doing engine (or browser detection), you’re still subject to the problems this article points out, but at least with engine detection you’re not joining this list of the clueless.
Sebastian Werner, I’m quite surprised that your Gecko detection routine works in Camino (and presumably other non-XUL Gecko browsers, though I don’t have any on hand to check against), and also that anything XUL-related is even exposed to web content. I’d be very wary of using anything related to XUL to definitively identify Gecko-based user-agents.
Nicholas Zakas, thanks so much for this great article pointing out all the problems with assumptions and mixing the two kinds of detection together!
Smokey Ardisson on January 1st, 2010 at 7:57 pm
[...] couple of weeks ago, I talked about feature detection and browser detection. That post featured a little bit about user-agent sniffing and the comments continued the trend. I [...]
History of the user-agent string | NCZOnline on January 12th, 2010 at 11:08 pm
[...] NCZ在他的同名博客《Feature detection is not browser detection》中,讲述了一直以来前端开发中的一个热门技术——检测用户的浏览器平台,并详细地叙说历史发展以及各种办法的优缺点。我大致翻译了部分文章,可能有理解错误的地方,敬请指正。值得一提的是,评论部分的争论亦值得一看。 [...]
特性检测并非浏览器检测 army8735 on March 7th, 2010 at 3:05 am
[...] in an incrementing fashion. Of course, you would first identify the client capabilities using feature detection in order to decide which line of code to [...]
A guide to CSS Opacity | Design the Code on April 1st, 2010 at 9:29 am
[...] the googlebot UA string when chasing SEO issues around our competitors. There was a nasty post by slicknet a while back in which he ripped into mootools for still using feature detection but I fail to see [...]
mootools 1.3 beta 1 is finally out for grabs | frag (frăg) on April 28th, 2010 at 7:30 am
Leave a Comment