jQuery sucks at browser detection
This week i've been tweaking a little site I've started called “ismybrowseruptodate.com“, which is designed to assist people who don't know whether their browser is up-to-date or not.
At first I thought this would be simple, because I knew jQuery had browser detection and had a variable called jQuery.browser.version, which would be very useful for checking the browser version, or so I thought.
Don’t get me wrong here, I think jQuery is a fantastic javascript framework and is great at helping you to avoid reinventing the wheel, but it sucks at browser detection!
On closer inspection I see that jQuery is deprecating this variable in place for better ways of browser detection, namely by not detecting the browser, but detecting technologies (via objects) instead.
As such, you can already see evidence of the jQuery team deprecating the browser sniffing by removing references to the jquery.browser variable.
The problem is that even now it still exists in jQuery because it is being used for backwards compatibility and by plugins. It can’t just be removed, so it must still be maintained.
As we know, jQuery sucks at browser detection because they’ve been detecting by browser, instead of by technologies all along, so what else is new?
Not long ago Opera Software released the latest version of their Opera Browser, which is version 10, however, during trials they came across some issues where some sites would detect the wrong version, forcing Opera to make changes to Opera’s user agent string.
Guess what?
jQuery is detecting the wrong version number for Opera. It’s detecting “Opera v10” as “Opera v9.80”.
However, in this instance it isn’t much of a problem because jQuery is moving it’s dependencies away from browser sniffing to technology detection, and opera made changes to their user agent string to handle it.
So what is the problem?
The problem is that jQuery is not maintained to cope with the changes to the user agent string and that they are getting engine versions confused with browser versions. Let’s take a look at the actual core code:
(userAgent.match( /.+(?:rv it ra ie)[\/: ]([\d.]+)/ ) [0,’0′])[1]
As you can see it has 4 checks…
- rv=Revision of the Gecko “layout engine”.
- it=Version of the WebKit “layout engine”.
- ra=Version of Opera, and NOT “Presto” it’s layout engine.
- ie=Version of Internet Explorer, but NOT “Trident” it’s layout engine.
After that you can see the browser checks it does…
safari: /webkit/.test( userAgent ),
opera: /opera/.test( userAgent ),
msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
mozilla: /mozilla/.test( userAgent ) && !/(compatible webkit)/.test( userAgent )
As you can see, it lists 4 browsers, some test for the layout engine, some test for the browser name, mozilla is a test for neither and what about Google Chrome?
The problem is that many browsers use the same layout engine, but may result in different version numbers.
So, the question is, are we detecting the web browser or the layout engine?
If it’s browsers, the version match is all wrong and doesn’t consider the changes made to Opera’s user agent string; and if it’s layout engines it’s detecting the wrong version numbers from the browser rather than the engine.
For jQuery at least, considering there are only about 20 notable layout engines, it would make more sense to match those rather than the literally hundreds of browsers, many of which are based on the same layout engine.
So how do we fix this?
It depends what you want to detect, whether it be the browser or the layout engine. The problem is that if you change this now, older code will no longer be compatible, as the problem with version numbers will arise.
I have raised this as a bug #5279 with the jQuery team, but I don’t expect much to be done about it.
Instead, I have created and am using a better browser sniffer on “ismybrowseruptodate.com” in javascript, however at the moment it is not open source code. If you are interested, let me know and I will release it as open source, along with the layout engine sniffer and detection I started developing too.
Enjoy!
// It's included for backwards compatibility and plugins,
// although they should work to migrate away.
browser: {
version: (/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/.exec(userAgent) || [0,'0′])[1],
safari: /webkit/.test( userAgent ),
opera: /opera/.test( userAgent ),
msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
}
Comments