Resources > Browser Sniffing

This gives information about client-side browser sniffing and other ways of making code which differs for different browsers.

Sometimes, to overcome browser differences, it is necessary to use different code for different browsers, although it is less necessary now than it was years ago, when fundamental browser differences were greater. Using different code should be done as seldom as possible — ideally never — but sometimes it cannot be avoided.

There are several ways of doing this, with varying levels of reliability, for different purposes:

Examples are given below.

Internet Explorer Conditional Comments

This technique enables different HTML to be parsed by IE, by different versions of IE, or by all browsers other than IE.

Microsoft introduced conditional comments with IE5 for Windows. They are supported by all versions of IE for Windows, from version 5 up, which is all versions of IE in common use today: 5.0, 5.01, 5.5, 6.0, and 7.0. It seems certain that they will be supported by future versions of IE.

The big advantage of conditional comments is that they are completely reliable, which is not something which can be said of other techniques.

The big disadvantages are that (a) they are HTML only, so can’t be put in CSS or JavaScript files, and (b) they hide the conditional HTML, so validators can’t see the HTML, allowing hidden errors to lurk in the HTML.

One use of conditional comments is to link to stylesheets which only certain versions of IE will see. For example, the following conditional comments appear in this page:

    <!--[if lt IE 6]>
        <link rel="stylesheet" type="text/css" href="style_ie5x.css">
    <![endif]-->

    <!--[if gte IE 5]>
        <link rel="stylesheet" type="text/css" href="style_ie.css">
    <![endif]-->

This links to one special stylesheet for IE 6 and under (the versions are specified by if lt IE 6), and to a second special stylesheet for all versions of IE (the versions are specified by if gte IE 5). The conditional comments follow HTML (not shown above) which links to the standard stylesheet, so the two IE stylesheets simply augment the standard stylesheet, typically to overcome IE defects, or to hide IE-specific CSS such as filters and scrollbar styling. One benefit of putting IE-specific CSS in IE-specific stylesheets is that this CSS will be hidden from CSS validators, which avoids messy validator error messages.

Microsoft documents conditional comments completely, though there is one detail which is commonly overlooked: to specify IE 5.5, it must be referred to as version 5.5000.

Note : if the trick discovered by Joe Maddalone of insert-title.com for installing multiple versions of IE is used, conditional comments will not work properly for the older versions of IE: this is a limitation of the trick, not a fault in IE, since this trick is not supported by Microsoft.

JavaScript Object Detection

This technique uses browser differences in JavaScript objects to do things differently in different browsers.

The big advantage of object detection is that it does not require that the browser be identified, which dispenses with problems there might be with faulty browser identification.

The big disadvantage is that one must take great care in examining objects: an object must not be used unless it exists, but the fact that it exists does not always means the object can be used. For example:

Object detection is typically used either to overcome a browser defect, or to take advantage of a browser feature which is not universal.

An example of object detection to overcome a browser defect appears in the above function, getElement(), which overcomes the failure of old versions of IE to support the standard method document.getElementById().

An example of detection to take advantage of browser features which are not universal would be detection of canvas.getContext() to determine whether the browser supports JavaScript related to the <canvas> tag, which is in the proposed HTML 5 standard, but not in any official standard.

CSS Tricks

This technique uses browser CSS differences to make different browsers see different CSS.

This is complex, and quite well described elsewhere, for example in Eric Meyer’s Tricking Browsers and Hiding Styles, so will not be discussed here further. Note that he mentions some tricks which apply only to extinct browsers.

Caution : some tricks depend on bugs in how browsers process invalid CSS, or on how well browsers support CSS features, so the tricks may not work, or may not work exactly the same, when browsers are updated.

JavaScript Browser Detection

This technique involves JavaScript code — commonly called a browser sniffer — which identifies the browser by examining certain JavaScript objects, chiefly the navigator.userAgent string.

This is the least reliable approach to dealing with browser differences, but it is more reliable than many give it credit for, and it often works well when other techniques cannot be applied. Browser sniffers have a sordid reputation, largely because of the widespread use of sniffers which are far too simplistic. The earliest sniffers dealt only with versions of IE and Netscape which today are extinct, e.g. IE 3-4 and Netscape 3-4: they could not handle the range of browsers which appeared later, and in many cases these sniffers were never updated, so many sites broke with newer browsers. Also, many sniffers which were updated to handle newer browsers are very naïve: e.g. they assume that, if 'MSIE' appears in the user agent string, the browser is Internet Explorer: this overlooks the fact that user agent strings of other browsers, such as Opera, sometimes contain 'MSIE'. Much better sniffers are possible.

The big disadvantage to browser sniffing, of course, is that it requires that JavaScript be enabled.

Another disadvantage is that it relies on the content of the navigator.userAgent string, and this string can be faked: e.g., someone using Firefox could use a plugin which gives it the same user agent string as IE. This second disadvantage is not as serious as one might think, however: someone would normally use such a plugin only for a site which has a sniffer so bad that the site cannot work with the right user agent; but no one would need to fake the user agent for a site with a good sniffer.

Note : Safari offers JavaScript libraries for determining the version of the AppleWebKit.

A Modest Browser Sniffer

This describes a browser sniffer I designed which I think is very good. It has two parts: the first part is HTML which uses conditional comments that set a variable only if the browser is IE; the second part is JavaScript which uses this variable and the navigator.userAgent string to set variables identifying the browser.

The HTML goes in the <head> section, and creates a variable isTrulyIE only if the browser is IE 5 (or later) for Windows:

    <!--[if gte IE 5]>
        <script type="text/javascript">
            var isTrulyIE = true;
        </script>
    <![endif]-->

The JavaScript, which should go in a JavaScript file which is loaded after this HTML, is:

    var agent = new Object();
    agent.ua = navigator.userAgent.toLowerCase();
    if ( typeof(isTrulyIE) != typeof(NoSuchVariable) )
      {
        agent.isIE = true;
        for ( var prop in [ 'Khtml', 'Safari', 'Opera', 'Gecko', 'Netscape', 'Webtv' ] )
            agent['is'+prop] = false;
      }
    else
      {
        agent.isKhtml = (agent.ua.indexOf("khtml") != -1);
        agent.isSafari = (agent.ua.indexOf("safari") != -1);
        agent.isOpera = (agent.ua.indexOf("opera") != -1);
        agent.isGecko = !agent.isKhtml && !agent.isSafari && !agent.isOpera
            && (agent.ua.indexOf("gecko/") != -1);
        agent.isNetscape = !agent.isKhtml && !agent.isOpera &&
          ( (agent.ua.indexOf('mozilla')!=-1)
            && ((agent.ua.indexOf('spoofer')==-1)
            && (agent.ua.indexOf('compatible')==-1)) );
        agent.isWebtv = (agent.ua.indexOf("webtv")!=-1);
        agent.isIE = !agent.isWebtv && !agent.isOpera && (agent.ua.indexOf("msie") != -1);
        agent.version = parseFloat(navigator.appVersion);
        if ( agent.isNetscape && (agent.version>=5) )
            agent.isGecko = true;
      }
    if ( agent.isIE )
        agent.version = parseFloat(agent.ua.substring(4+agent.ua.indexOf("msie")));
    else if ( agent.isOpera )
      {
        if ( agent.ua.indexOf("opera/") == 0 )
            agent.version = parseFloat(agent.ua.substring(1+agent.ua.indexOf("/")));
        else if ( agent.ua.indexOf("opera/") != -1 )
            agent.version = parseFloat(agent.ua.substring(2+agent.ua.indexOf(")")));
        else
            agent.version = parseFloat(agent.ua.substring(6+agent.ua.indexOf("opera")));
      }

The above creates an object, agent, which has the following properties:

The above code reliably detects IE, with 100% certainty due to the dependence on conditional comments, for IE 5 (and up) for Windows, even if the user agent has been faked. If the browser is another version of IE, there is a tiny chance that the browser might be wrongly identified; but those versions of IE are extinct, so this exception is hardly critical.

Identification of other browsers will be extremely reliable: not 100%, but close. This is partly because the certain detection of IE eliminates a lot of possible errors, but also because the code is very careful in analyzing the user agent: for example, before checking to see if the user agent contains 'gecko/', it excludes browsers whose user agents may mimic Gecko user agents, e.g. Opera. The code is likely to misidentify the browser only if the browser is not IE and the entire user agent string has been faked: and, as pointed out above, the user would have no need to fake the user agent string for a site with a good browser sniffer; in practical terms, therefore, this code is exceedingly reliable.

The least reliable information is the version number: it should be correct for IE or Opera; but for other browsers it depends on navigator.appVersion. More reliable identification of the version is possible: I have code to do it, but is not listed here because it rarely matters, since few people use very old versions of browsers, except people who use IE.

This browser sniffer detects a wider range of browsers than many sites will need to recognize, e.g. Safari and MSN-TV (WebTV). This is deliberate: a key concept of the above code is that it is a standard block of code which can be plugged into any site, and which can be safely replaced by a newer block of code if the sniffer ever has to be updated. The slight overhead of detecting things which are not needed for some sites is a small price for having a standard module.

Examples

Here are some examples of using different code for different browsers:

Linking to IE Stylesheets

One way to specify different CSS rules for IE, or for different versions of IE, is to link to one master stylesheet file which defines the standard rules for all browsers, and then to use conditional comments to link to other, much smaller stylesheet files which set a few rules, or override a few rules, to deal with the vagaries of various versions of IE. For example:

    <link rel="stylesheet" type="text/css" href="style.css" />

    <!--[if lt IE 6]>
        <link rel="stylesheet" type="text/css" href="style_ie5x.css">
    <![endif]-->

    <!--[if gte IE 5]>
        <link rel="stylesheet" type="text/css" href="style_ie.css">
    <![endif]-->

This links to the standard stylesheet, style.css. If the browser is IE 5.x for Windows, this then links to style_ie5x.css. If the browser is any version of IE for Windows from 5.0 up, this then links to style_ie.css.

This is completely reliable for IE for Windows 5 and up.

JavaScript for a Particular Version of IE

Sometimes JavaScript is needed which is different for a specific version of IE. For example, I have several sites with JavaScript that generates HTML to produce CSS-styled bar charts, but it does not work well for IE 5.0x because its poor CSS support produces ugly results. What I did for these sites is use conditional comments to set a JavaScript variable calBarCharts, i.e:

    <!--[if lt IE 5.5000]>
        <script type="text/javascript">
            var calBarCharts = false;
        </script>
    <![endif]-->

In some cases the bar charts were not critical and could be omitted, so the JavaScript generating the HTML would generate nothing if the variable existed and was false.

In some cases the bar charts were more important, so the JavaScript generating the HTML would generate different HTML, producing less attractive but adequate bar charts, if the variable existed and was false.

In either case, using conditional comments for this purpose eliminates the need for a complex browser sniffer. This is completely reliable for IE for Windows 5 and up.

Scrolling to a Bookmark

Sometimes JavaScript is needed which can scroll to a bookmark on the current page. Some browsers, however, do not support the preferred method, so an alternate method is used for these browsers. In the following function, object detection is used to decide which method to use:

function myScrollToAnchor ( id )
{
    if ( arguments.length < 1 )
        id = window.location.hash;
    if ( id.indexOf('#') == 0 )
        id = id.substring(1);
    var o = getElement( id );
    if ( o )
      {
        if ( (typeof(o.offsetTop) !== typeof(nosuchvalue))
          && (typeof(window.scrollTo) !== typeof(noSuchFunction)) )
          {
            var x = 0;
            var y = 0;
            while ( o )
              {
                x += o.offsetLeft;
                y += o.offsetTop;
                o = o.offsetParent;
              }
            window.scrollTo( x, y );
          }
        else
            window.location.replace( '#' + id );
      }
}

The function takes an optional argument, the ID for the bookmark. If the argument is omitted, the bookmark is extracted from the URL for the current page.

Object detection is done in two locations, marked above by gold underlines:

The above should be reliable unless the browser supports neither method: I am not aware of any which don’t.

Caution : the above function has been observed to fail with certain old browsers if the function is called just after the HTML containing the anchor has been dynamically changed, e.g. using innerHTML. It appears that the browser tries to scroll to the bookmark before the dynamic changes have completed.

Extracting the Filename from a Pathname

Consider the problem of extracting a filename from a pathname. The following function was created to do this, which uses a method akin to object detection to do it:

function myGetFilename ( pathname )
{
    var sFilename;
    if ( (arguments.length == 0) || (pathname == null) )
        pathname = document.location.pathname;
    var nSlash = pathname.lastIndexOf( '/' );
    if ( nSlash == -1 )
        sFilename = pathname;
    else
        sFilename = pathname.substring(nSlash+1);
    nSlash = pathname.lastIndexOf( '\\' );
    if ( nSlash != -1 )
        sFilename = sFilename.substring(nSlash+1);
    return( sFilename );
}

This function takes a pathname as an optional argument: if the argument is omitted or null, the pathname to the current document is used.

The basic algorithm looks for the last '/' in the pathname: if not found, the filename is the pathname; if found, the filename is the string to the right of the '/'. However, some older versions of IE use a '\' instead of a '/' to separate directory names and the filename in a pathname: so the function then looks for the last '\' in the pathname: if not found, the filename is the pathname; if found, the filename is the string to the right of the '\'. This enables the function to work no matter which browser is used: not exactly by object detection, but, like object detection, the identity of the browser need not be determined, as it suffices simply to recognize the different behaviours of different browsers.

Setting Basic Font Sizes

When using CSS to style text, one common thing to do is set the basic font size. I often prefer to do so using a method which honours the user’s preferred font size, but which sets the size one step smaller. With CSS, one should be able to do this using something like:

html { font-size:small; }

Unfortunately, this assumes that the default size is font-size:medium, which is what it is supposed to be, but this is not true of IE: for IE the default size is font-size:small. This means that, to get consistent results, setting the size one step smaller for IE requires:

html { font-size:x-small; }

This can be achieved for all browsers using a CSS trick which depends on how IE processes certain invalid CSS, thusly:

html { font-size:x-small; voice-family: "\"}\""; voice-family:inherit; font-size:small; }

Experience over the years indicates that this works well, but there is always a risk that a new version of IE will not work as expected, because this method depends on IE misbehaving in a certain way.

 

 Top of Page   Legal Notices