Browser-detection (Advice)

Browser-detection techniques require some review in the light of recent browser upgrades.

Browser-detection techniques have been in use ever since the MSIE version 3 browser was launched. Since the features of this browser differed from others, it became necessary to determine what browser was being used to establish the features actually available.

The subject of browser detection is now extremely complex, with a wide variety of browsers. The technique of simply distinguishing between Netscape and MSIE is no longer sufficient. This is even more of an issue now that Netscape version 6.0 is shipping.

This Netscape version 6.0 browser was developed around a completely new source code base. Having started completely afresh, Netscape has not implemented any unnecessary legacy features and instead has pursued a strictly standards-based approach. The most noticeable effect of this is the complete lack of support for layers.

The reason why this is such a big problem is that in the past we might typically have written a short script to determine whether we were running Netscape or MSIE, and coded accordingly. The first example shows how we would have done this using a classical detection technique.

However, now we have millions of web pages using this technique that also use layers. They will detect Netscape 6.0 and return a value saying "OK it's Netscape, so use the Netscape layers code alternative". This is going to break a lot of pages.

We now need something that tells us whether a particular feature is available rather than a particular browser. The second example is a skeleton of how we might do that. It adds a member object called isAvailable to the global object, that can have additional properties added as we need to extend it. In this example, it simply provides access to whether layers are available or not. It provides for three possible cases:

So now we can build some code to exploit this using a switch statement thus:

   isAvailable();
   switch(isAvailable.Layers)
   {
      case "YES" : // Call the layer programmed version of our page
         break;
      case "DIV" : // Call the <DIV> simulated layers version of the page
         break;
      case "NO" : // Fall back to the legacy browser, no layers page
         break;
      default: // Unexpected condition handled here
         break;
   }

Because we need to know what browser and version the code is being run on to determine simulation capabilities, those get set as member properties of the isAvailable object.

Later you could add capabilities to access the DOM feature-detection mechanisms so that all these related feature detection facilities are in a single reusable code block.

Note that the appVersion value picks up the Mozilla/X.YY value and uses that. This means that MSIE 5 reports an appVersion of 4 and Netscape version 6.0 reports an appVersion of 5, which is odd to say the least. You can do a bit more work to parse out the correct version numbers from the remainder of the user agent string or access special values that are platform-dependent within a fragment of code that is selected on a per platform basis.

Example code:

   <SCRIPT>
   // Classic browser detection returning browser types
   // and versions
   function getBrowserType()
   {
      var myUserAgent;
      var myMajor;
      myUserAgent   = navigator.userAgent.toLowerCase();
      myMajor       = parseInt(.appVersion);
      if( (myUserAgent.indexOf('mozilla')    != -1) &&
         (myUserAgent.indexOf('spoofer')    == -1) &&
         (myUserAgent.indexOf('compatible') == -1) &&
         (myUserAgent.indexOf('opera')      == -1) &&
         (myUserAgent.indexOf('webtv')      == -1))
      {
         if (myMajor > 4)
         {
            return "nav6";
         }
         if (myMajor > 3)
         {
            return "nav4";
         }
         return "nav";
      }
      if (myUserAgent.indexOf("msie") != -1)
      {
         if (myMajor > 4)
         {
            return "ie5";
         }
         if (myMajor > 3)
         {
            return "ie4";
         }
         return "ie";
      }
      return "other";
   }
   </SCRIPT>
   <SCRIPT>
   isAvailable();
   document.write("Browser family : "  + isAvailable.Browser + "<BR>");
   document.write("Browser version : " + isAvailable.Version + "<BR>");
   document.write("Layer support : "   + isAvailable.Layers  + "<BR>");
   // Modern extensible browser feature detection
   // Becomes a member property of the global object
   function isAvailable()
   {
      // Get user agent stuff
      var myUserAgent   = navigator.userAgent.toLowerCase();
   
      // Set initial conditions
      isAvailable.Browser = "OTHER";
      // Check for navigator
      if( (myUserAgent.indexOf('mozilla')    != -1) &&
         (myUserAgent.indexOf('spoofer')    == -1) &&
         (myUserAgent.indexOf('compatible') == -1) &&
         (myUserAgent.indexOf('opera')      == -1) &&
         (myUserAgent.indexOf('webtv')      == -1))
      {
         isAvailable.Browser = "NAV";
      }
      // Check for MSIE
      if (myUserAgent.indexOf("msie") != -1)
      {
         isAvailable.Browser = "MSIE";
      }
      // Store major version number from leading Moziilla/X.YY
      // Portion of user agent string
      isAvailable.Version = parseInt(navigator.appVersion);
   
      // Work out if layers available
      if(document.layers)
      {
         isAvailable.Layers = "YES";
      }
      else
      {
         if((isAvailable.Browser = "MSIE") && (isAvailable.Version > 3) ||
            (isAvailable.Browser = "NAV")  && (isAvailable.Version > 4))
         {
            isAvailable.Layers = "DIV";
         }
         else
         {
            isAvailable.Layers = "NO";
         }
      }
   }
   // For further investigation look at the MSIE script engine version and build number properties and map them to features.
   </SCRIPT>