Click Here!
|
The Ultimate JavaScript
Client Sniffer, Version 3.0:
Determining Browser Vendor, Version, and Operating System With JavaScript
When creating a web page, you may need to account for the possibility that viewers will be using browsers of various versions from various vendors. You may also need to account for the possibility that certain page elements such as native plug-ins may not be available on all operating systems.
This sample code enables you to detect the browser's vendor, version number, and operating system. It has been tested on Navigator 2, 3, 4, and 5 (developer preview), Internet Explorer 3, 4, and 5, and Opera 3 on Windows 95, Windows NT, the Macintosh, and SunOS5. It is believed to be compatible with all JavaScript-capable browser versions on all platforms. It creates an group of variables whose names all begin with "is_". These variables indicate the browser's vendor, version number, JavaScript version, and operating system.
After you have checked the browser vendor and version, you can dynamically generate optimized HTML markup and your JavaScript code can conditionally branch to execute JavaScript code optimized for the dynamically generated page or the current browser's vendor or version number.
This tip is useful when developing a page with features not supported by all browsers. For example, users who are using Navigator 4 can view content that includes the LAYER tag. Other users can view a "non-layers" version of the same file.
As another example, Navigator 4 and Internet Explorer 4 often implement the same Dynamic HTML functionality with different objects and methods. After checking the browser vendor, you can conditionally execute the correct JavaScript code fork for the current browser. And with the Gecko layout engine in Navigator 5, you can for the first time write code using the W3C standard DOM Level 1.
When optimizing a page for multiple browsers, it is sometimes convenient to check the browser vendor and/or version and then use document.write() statements in <SCRIPT> elements in the page's BODY to dynamically generate HTML markup which is optimized for the current browser. Here is an example of the code necessary to dynamically generate vendor- or version-specific HTML markup. This code uses the "is_" variables defined by the sample JavaScript code below to check browser vendor and version.
Note: to make sure that
our code works on future versions of Navigator, we use the variable is_nav4up
instead of is_nav4. To make sure that our code works on future versions
of Internet Explorer, we use the variable is_ie4up instead of is_ie4. When
writing conditional code forks, always keep forward compatibility with
future browser versions in mind!
In your page's core JavaScript
functions, you can then have vendor- and version-specific code forks where
necessary. These code forks reference objects, properties, methods, and
functions which are only available in specific browser versions. The code
forks may also reference HTML page elements whose HTML markup is dynamically
generated only for certain browser versions.
This code fragment uses the
"is_" variables defined by the sample JavaScript code below to check browser
vendor and version and then conditionally evaluate different code forks.
Note: to make sure that
our code works on future versions of Navigator, we use the variable is_nav4up
instead of is_nav4. To make sure that our code works on future versions
of Internet Explorer, we use the variable is_ie4up instead of is_ie4. When
writing conditional code forks, always keep forward compatibility with
future browser versions in mind!
No doubt you've heard of
the Y2K bug, in which computer software not designed to handle years later
than 1999 fails with errors in the year 2000. But have you heard of the
V5.0 bug?
In the V5.0 bug, JavaScript
code not designed for browsers versions later than 4.x fails with errors.
Just as you should review your software before the year 2000 to ensure
it won't fail when the year changes, you should review your software before
using Internet Explorer 5 and Navigator 5 to make sure it won't fail on
newer browsers.
The V5.0 bug can be caused
by a number of common mistakes. Review your code now to make sure you don't
fall victim to these problems:
1. If your code works
in browser version N and later, make certain to check browser version with
>= instead of ==.
For example, suppose that
you have code which works in Navigator 4 and later and Internet Explorer
4 and later. Compare these two approaches to client sniffing:
<SCRIPT>
<!--
if (is_nav5up)
{
// document.write() statements to create markup for Navigator 5 and later using HTML 4.0
}
else if (is_nav4)
{
// document.write() statements to create markup for Navigator 4
}
else if (is_ie4up)
{
// document.write() statements to create markup for IE 4 and later versions
}
else
{
// document.write() statements to create static HTML markup for earlier versions
}
// -->
</SCRIPT>
BROWSER-SPECIFIC JAVASCRIPT CODE FORKS
if (is_nav5up)
{
// JavaScript here for Navigator 5 and later using the W3C DOM
}
else if (is_nav4)
{
// JavaScript here for Navigator 4
}
else if (is_ie4up)
{
// JavaScript here for IE 4 and later
}
else if (is_nav3 || is_opera)
{
// JavaScript here for Nav3 and Opera
}
else
{
// JavaScript here for Nav2 and IE 3
}
CLASSIC MISTAKE #1 IN CLIENT DETECTION: FORGETTING ABOUT FUTURE BROWSER VERSIONS
WRONG WAY | RIGHT WAY |
if (is_nav4 || is_ie4) { /* Code which works on Nav4+, IE4+. Because the if clause only returns true for Nav4 and IE4, the code will never be executed on Nav5 and IE5 and later versions. Oops! Should have used is_major >= 4. */ } | if (is_nav4up || is_ie4up) { /* Code which works on Nav4+, IE4+. Because the if clause returns true for Nav4 and later and IE4 and later, the code will be executed on all the browsers which support it. */ } |
Here's another example which
uses the variable is_major from the below sample code:
WRONG WAY | RIGHT WAY |
if (is_major == 4) { /* Code which works on Nav4+, IE4+. Because the if clause only returns true for Nav4 and IE4, the code will never be executed on Nav5 and IE5 and later versions. Oops! Should have used is_major >= 4. */ } | if (is_major >= 4) { /* Code which works on Nav4+, IE4+. Because the if clause returns true for Nav4 and later and IE4 and later, the code will be executed on all the browsers which support it. */ } |
2. If your code needs to be updated for newer browser versions or the W3C DOM, build in support and a version check now.
The Gecko layout engine will make Navigator 5 the first browser to fully support key standards such as the W3C Document Object Model Level 1 (DOM1). Up until now, web developers who wished to write sophisticated JavaScript applications or use Dynamic HTML had to use the Navigator 4 DOM and the Internet Explorer 4 DOM. Both of these DOMs were proprietary, non W3C DOM-compliant, and incompatible with each other. The difficulty, frustration, and expense of developing and debugging the same functionality twice for different DOMs has led web developers to call on all browser vendors to fully support the W3C DOM so that web developers can write once and run anywhere.
Navigator 5 will at last offer the W3C standards support which developers have been demanding. To take full advantage of the power and cross-platform compatibility of the W3C DOM, you will need to upgrade your application to support it. While it will take some time to learn the W3C DOM, keep in mind that you will at last be studying a vendor-independent, platform-independent, application-independent Document Object Model. The W3C DOM is the DOM for the 21st century. Learning to use it will put you on the cutting edge of modern web site design and develop skills you can use for the rest of your career.
One popular variation on client detection is object detection. Fans of object detection argue that since new clients appear all the time, rather than explicitly detecting the client and then using the particular client's objects and methods, you should simply check to see whether the object you want exists, and if so, use it.
This approach is fine so long as you take it to its logical extreme and explicitly test for the existence of every single object and method before you use it.
The problem with this approach is that many people do the following:
WRONG WAY | RIGHT WAY |
if (document.layers) {
/* Code which uses lots of other Nav4 features besides document.layers, without testing for those objects and methods as well. Oops! Should have detected each object and method one at a time before using. */ } else if (document.all) { /* Code which uses lots of other IE4 features besides document.all, without testing for those objects and methods as well. Oops! Should have detected each object and method one at a time before using. */ } else if (document.getElementById) { /* Code which uses lots of other Nav5 or W3C DOM1 features besides document.getElementById, without testing for those objects and methods as well. Oops! Should have detected each object and method one at a time before using. */ } |
if (document.layers) {
/* Code which uses only document.layers, because that's all we're sure exists after testing for it. Good object detection code detects each other object and method one at a time before using it. */ } else if (document.all) { /* Code which uses only document.all, because that's all we're sure exists after testing for it. Good object detection code detects each other object and method one at a time before using it. */ } else if (document.getElementById) { /* Code which uses only document.getElementById, because that's all we're sure exists after testing for it. Good object detection code detects each other object and method one at a time before using it. */ } |
The reason the code in the left column is a mistake is that there is no guarantee that you are running on Internet Explorer 4 or later if document.all is defined, there is no guarantee that you are running on Netscape Navigator 4 if document.layers is defined, and there is no guarantee that you are running on Navigator 5 or another fully DOM1-compliant browser if document.getElementById is defined.
New browsers (such as Opera and iCAT) appear all the time. Browsers not made by Netscape or Microsoft may feature a hybrid DOM that is a mix of Navigator's and Internet Explorer's two DOMs. For example, a non-Netscape, non-Microsoft browser DOM might support both document.layers and document.all, yet not support all of the other features of either browser. If you wrote code that tested for the existence of document.all and then (if document.all was defined) used many IE features besides document.all, that code would fail with errors on any browser which supported document.all but not other IE features. Similarly, if you wrote code that tested for the existence of document.layers and then (if document.layers was defined) used many Navigator 4 features besides document.layers, that code would fail with errors on any browser which supported document.layers but not other Navigator 4 features.
New versions of existing browsers (such as IE5 or Nav5) also appear all the time. Finally, Navigator 5 will be the first browser to support DOM1 features such as document.getElementById, but it will not be the only browser to support DOM1. Other browsers should add support for the DOM1 over time. However, they might not all match Navigator 5's level of support for DOM1. So if you detect document.getElementById, all you know for sure is that the current browser supports the getElementById of the document object. You don't know for sure that you're running on Navigator 5.
The moral of the story: If you detect a particular object, all you've done is detect that particular object. You don't know for sure which browser you're running on. All you know after detecting a particular object is that the particular object exists on the current client.
Here are two Internet Explorer quirks of the navigator.appVersion property to be aware of:
AOL userAgent strings are supposed to always include the string "AOL #.#", where the pound signs stand for the version number such as 3.0 or 4.0. This is in fact true for the user agent string reported in the HTTP header; it will always contain "AOL #.#".
However, the navigator.userAgent string that is reported by JavaScript has a bug in AOL 4.0 where "AOL #.#" will not be inserted into the reported user agent string when:
Here is the JavaScript code necessary to detect browser vendor, version number, and operating system. This code creates a group of variables which indicate the browser's vendor, version number, JavaScript version, and operating system.
This code is believed to be compatible with all versions of all JavaScript-capable browsers on all platforms. It has been tested on the following operating systems and browser versions:
// *** PLATFORM *** var is_win = ( (agt.indexOf("win")!=-1) || (agt.indexOf("16bit")!=-1) ); // NOTE: On Opera 3.0, the userAgent string includes "Windows 95/NT4" on all // Win32, so you can't distinguish between Win95 and WinNT. var is_win95 = ((agt.indexOf("win95")!=-1) || (agt.indexOf("windows 95")!=-1)); // is this a 16 bit compiled version? var is_win16 = ((agt.indexOf("win16")!=-1) || (agt.indexOf("16bit")!=-1) || (agt.indexOf("windows 3.1")!=-1) || (agt.indexOf("windows 16-bit")!=-1) ); var is_win31 = ((agt.indexOf("windows 3.1")!=-1) || (agt.indexOf("win16")!=-1) || (agt.indexOf("windows 16-bit")!=-1)); // NOTE: Reliable detection of Win98 may not be possible. It appears that: // - On Nav 4.x and before you'll get plain "Windows" in userAgent. // - On Mercury client, the 32-bit version will return "Win98", but // the 16-bit version running on Win98 will still return "Win95". var is_win98 = ((agt.indexOf("win98")!=-1) || (agt.indexOf("windows 98")!=-1)); var is_winnt = ((agt.indexOf("winnt")!=-1) || (agt.indexOf("windows nt")!=-1)); var is_win32 = (is_win95 || is_winnt || is_win98 || ((is_major >= 4) && (navigator.platform == "Win32")) || (agt.indexOf("win32")!=-1) || (agt.indexOf("32bit")!=-1)); var is_os2 = ((agt.indexOf("os/2")!=-1) || (navigator.appVersion.indexOf("OS/2")!=-1) || (agt.indexOf("ibm-webexplorer")!=-1)); var is_mac = (agt.indexOf("mac")!=-1); var is_mac68k = (is_mac && ((agt.indexOf("68k")!=-1) || (agt.indexOf("68000")!=-1))); var is_macppc = (is_mac && ((agt.indexOf("ppc")!=-1) || (agt.indexOf("powerpc")!=-1))); var is_sun = (agt.indexOf("sunos")!=-1); var is_sun4 = (agt.indexOf("sunos 4")!=-1); var is_sun5 = (agt.indexOf("sunos 5")!=-1); var is_suni86= (is_sun && (agt.indexOf("i86")!=-1)); var is_irix = (agt.indexOf("irix") !=-1); // SGI var is_irix5 = (agt.indexOf("irix 5") !=-1); var is_irix6 = ((agt.indexOf("irix 6") !=-1) || (agt.indexOf("irix6") !=-1)); var is_hpux = (agt.indexOf("hp-ux")!=-1); var is_hpux9 = (is_hpux && (agt.indexOf("09.")!=-1)); var is_hpux10= (is_hpux && (agt.indexOf("10.")!=-1)); var is_aix = (agt.indexOf("aix") !=-1); // IBM var is_aix1 = (agt.indexOf("aix 1") !=-1); var is_aix2 = (agt.indexOf("aix 2") !=-1); var is_aix3 = (agt.indexOf("aix 3") !=-1); var is_aix4 = (agt.indexOf("aix 4") !=-1); var is_linux = (agt.indexOf("inux")!=-1); var is_sco = (agt.indexOf("sco")!=-1) || (agt.indexOf("unix_sv")!=-1); var is_unixware = (agt.indexOf("unix_system_v")!=-1); var is_mpras = (agt.indexOf("ncr")!=-1); var is_reliant = (agt.indexOf("reliantunix")!=-1); var is_dec = ((agt.indexOf("dec")!=-1) || (agt.indexOf("osf1")!=-1) || (agt.indexOf("dec_alpha")!=-1) || (agt.indexOf("alphaserver")!=-1) || (agt.indexOf("ultrix")!=-1) || (agt.indexOf("alphastation")!=-1)); var is_sinix = (agt.indexOf("sinix")!=-1); var is_freebsd = (agt.indexOf("freebsd")!=-1); var is_bsd = (agt.indexOf("bsd")!=-1); var is_unix = ((agt.indexOf("x11")!=-1) || is_sun || is_irix || is_hpux || is_sco ||is_unixware || is_mpras || is_reliant || is_dec || is_sinix || is_aix || is_linux || is_bsd || is_freebsd); var is_vms = ((agt.indexOf("vax")!=-1) || (agt.indexOf("openvms")!=-1));
Here are the results of running that JavaScript code on the browser you are using. The below text has been dynamically generated after checking your browser vendor, version, and operating system from JavaScript.
Basic Data
Version Number
Browser Version
JavaScript Version
OS
You're probably wondering
why we created a bunch of variables with similar names instead of doing
something more elegant like this:
function Is () { var agt=navigator.userAgent.toLowerCase(); this.major = parseInt(navigator.appVersion); this.minor = parseFloat(navigator.appVersion); this.nav = ((agt.indexOf('mozilla')!=-1) && ((agt.indexOf('spoofer')==-1) && (agt.indexOf('compatible') == -1))); this.nav2 = (this.nav && (this.major == 2)); this.nav3 = (this.nav && (this.major == 3)); ... and so on ... this.vms = (agt.indexOf("vax")!=-1) || (agt.indexOf("openvms")!=-1); } var is = new Is(); if (is.nav) { ... navigator code here ... } else if (is.ie) { ... explorer code here ... }The answer is simple: this code is far more elegant, providing encapsulation of the variables with an enclosing "is" object, but it breaks on Internet Explorer 3 for the Macintosh. If you create an "is" object on IE3 for the Mac, the first time the page is loaded, the code will work fine, however any reloads of the page will cause the browser to crash. To get around this on IE3 for the Mac, we don't create an "is" object; instead, we create bunch of boolean variables which have similar names. This is ugly, but it's the price we pay for working around this bug of JScript for the Macintosh in IE3.
Another possible workaround is to use the object oriented code on all other browsers but wrap it in a check which avoids executing the object oriented code on IE3 for the Mac. This preserves the object oriented design of the code but requires an extra boolean check like
if (!isIE3Mac && is_nav4up) each time you reference the is object. As this extra boolean check is inconvenient, we've resigned ourselves to the simple non-object oriented version above. However, if you prefer the object oriented approach, we provide a object oriented client sniffer with a safety check for the Mac. Finally, if you don't need to support IE3 for the Mac, you can use that object oriented code and omit the Mac safety check.
DevEdge Online FAQ Developer Response Center Join DevEdge Program |
This site powered by: Netscape Enterprise Server and Netscape Compass Server. |