Wednesday, June 23, 2021

Browser and Feature Detection: Make Your Website Look Great Everywhere

By Sascha P. Corti

 

A bit of history

Today, all web browsers are built with one common goal in mind: Render webpages optimally according to the latest specifications.

This wasn’t always so. In the past, most browsers implemented high-demand features that were not yet standardized. Each browser did so in its own way, like, for example, the possibility to set transparency in CSS.

Internet Explorer, before v.8, understood the following CSS:

  .transparent {

      /* Internet Explorer < 9 */

      width: 100%;

      filter: alpha(opacity=50);

  }

Firefox had its own attribute:

  .transparent {

      /* Firefox < 0.9 */

      -moz-opacity:0.5;

  }

As did Safari:

  .transparent {

      /* Safari < 2 */

      -khtml-opacity: 0.5;

  }

Today, however, in CSS3, there is a unified way of setting the transparency of an element:

  .transparent {

      /* IE >= 9, Firefox >= 0.9, Safari >= 2, Chrome, Opera >= 9 */

      opacity: 0.5;

  }

It can certainly be seen as a good thing when browsers go the “extra mile” to support new features quickly. But when those features aren’t standardized, it definitely makes life harder for developers, since we have to account for all the different implementations of each feature.

 

Same Markup

The best way to make sure your web page will render optimally in all browsers is to use markup that is supported in all current browsers. Until very recently, this was HTML 4.01, a 10-year-old standard with very limited features.

Today, all browsers are converging toward the feature-rich HTML5. However, many of the new specifications that fall under the term “HTML5” (including HTML5 markup, its APIs including DOM Levels 2 and 3, CSS3, SVG, and EcmaScript 262) are still in development and subject to change.

Browser vendors are continuously adding support for HTML5 features, but at quite different paces.

Firefox and Opera are usually quick to adopt new HTML5 specifications—even some that are in early development, subject to change, or have security issues. 

On the upside, it’s great that developers get a chance to test new features. However, this early adoption rate often leads to sites that use features that break pages between browser releases. An example of this was Firefox 4 disabling Websockets between Beta 7 and 8 due to security reasons. It’s an extremely frustrating experience for both users and developers

Chrome—which is also quickly adopting new HTML5 standards—has recently managed to stir the HTML5 community with its announcement to abandon support for the popular h.264 video codec for HTML5 <video> elements and instead switch to the royalty free WEBM standard. While this is not a bad decision for developers that currently pay for h.264 licenses, it again adds more choices that developers will have to keep track on what video formats they will need to encode and host their content in order to support as many browsers as possible.

Microsoft doesn’t implement standards as quickly, but does work closely with the W3C to build test suites. This collaboration helps to:

  • Minimize ambiguity in the specifications
  • Build technical foundations for browser vendors to work on
  • And homogenize how browsers render HTML5. 

 

To see their latest work in this area, have a look at Internet Explorer 10 Platform Preview that’s available on IE Test Drive.

You can also check out the HTML5labs, where Microsoft prototypes early and unstable specifications from web standards bodies such as the W3C. See Improved Interoperability Through Standards Support for in-depth information on how Internet Explorer 9 supports the various HTML5 specifications today.

But since the new HTML5 standards are still a moving target—and most Internet users don’t use the latest version of any given browser—serving the right markup is more important than ever.

 

Browser Detection

One approach to cover browser differences is by using browser detection. The most common approach is to use JavaScript to query the user-agent header:

  <script type=”text/javascript”>

      if ( navigator.userAgent.indexOf(“MSIE”)>0 )

      {

          // run custom code for Internet Explorer.

      }

  </script>

 

But there are a couple of problems with doing it this way.

First, it bundles multiple assumptions of which features the browser supports in one single check. A single wrong assumption can break the site. So, as a developer, you have to keep track of which features are supported by which browsers.

The second issue is that the browser check above doesn’t take browser versions into consideration, so it isn’t future-proof. So even if it works with today’s version of a browser, the next release might not require—or worse, might remove support altogether for—a workaround that the browser detection was used to add to the site.

If you have to use browser detection, make sure to only use it to detect legacy browsers as shown in the following example, and always keep in mind the version of the browser you are checking for.

  <script type=”text/javascript”>

      function getInternetExplorerVersion()

      // Returns the version of Internet Explorer or a -1 for other browsers.

      {

          var rv = -1;

          if (navigator.appName == ‘Microsoft Internet Explorer’)

          {

              var ua = navigator.userAgent;

              var re  = new RegExp(“MSIE ([0-9]{1,}[\.0-9]{0,})”);

              if (re.exec(ua) != null)

              rv = parseFloat( RegExp.$1 );

          }

          return rv;

      }

 

      function onLoad()

      {

          var version = GetInternetExplorerVersion()

          if (version <= 7 && version > -1)

          {

              // Code to run in Internet Explorer 7 or less.

          }

      }

  </script>

 

MSDN has a great article with more information if you’re interested: “Detecting Browsers More Effectively”.

 


 

Using breakpoints while debugging JavaScript in Internet Explorer 9.

 


 

Running in “Document Mode: IE9 standards,” the browser uses the modern “addEventListener” method.

 


 

Running in “Document Mode: IE7 standards,” the debugger falls back to the legacy “attachEvent” method.

 


Change Internet Explorer’s user agent string on the fly using the developer tools and even add your own ones, including mobile browsers.

 

Managing Feature Detection in large projects

When creating a complex web project, creating and managing all the feature detection code yourself can be tedious.

Luckily, there are great JavaScript libraries available that help in this effort, namely Modernizr and jQuery.

Modernizr already has built-in detection for most HTML5 and CSS3 features that are very easy to use in your code. Modernizr is very widely adopted and constantly enhanced. Both Modernizr and jQuery are shipped with the ASP.NET MVC tools.

Take a look at the following code to detect the browser’s capability to display web fonts:

Without Modernizr

With Modernizr

function(){

  var

    sheet, bool,

    head = docHead || docElement,

    style = document.createElement(“style”),

    impl = document.implementation || { hasFeature: function()

      { return false; } };

 

    style.type = ‘text/css’;

    head.insertBefore(style, head.firstChild);

    sheet = style.sheet || style.styleSheet;

    var supportAtRule = impl.hasFeature(‘CSS2’, ”) ?

      function(rule) {

        if (!(sheet && rule)) return false;

          var result = false;

          try {

            sheet.insertRule(rule, 0);

            result = (/src/i).test(sheet.cssRules[0].cssText);

            sheet.deleteRule(sheet.cssRules.length – 1);

          } catch(e) { }

          return result;

        } :

        function(rule) {

          if (!(sheet && rule)) return false;

          sheet.cssText = rule;

 

          return sheet.cssText.length !== 0 &&

            (/src/i).test(sheet.cssText) &&

            sheet.cssText

              .replace(/\r+|\n+/g, ”)

              .indexOf(rule.split(‘ ‘)[0]) === 0;

        };

    bool = supportAtRule(‘@font-face { font-family: “font”;

    src: url(data:,); }’);

    head.removeChild(style);

    return bool;

  };

<script type=”text/javascript” src”modernizr.custom.89997.js”></script>

 

<script type=”text/javascript”>

  if (Modernizr.fontface){

    // font-face is supported

  }

</script>

 

“Help!” What to do when a browser doesn’t support the feature you need

If the browser tested doesn’t support a feature you need, feature detection won’t take the burden of coming up with a workaround from you. Sorry.

In the HTML5 video sample above, using Silverlight as a fallback was an obvious solution. But what about other HTML5 features like <canvas> or the new semantic tags in HTML5 including <nav>, <section> and <article>, <aside> or the new <header> and <footer>?

 

There are a growing number of ready-made “fallbacks” for most HTML5 features, called shims and polyfills. Shims and polyfills are CSS and JavaScript libraries (or sometimes Flash or Silverlight controls) that you can add to your project, which will then add in the missing, unsupported HTML5 features.

The general idea is that developers should be able to develop with the HTML5 APIs, and scripts can create the methods and objects that should exist. Developing in this future-proof way means as users upgrade, your code doesn’t have to change—users will seamlessly move to the better, native experience.

The difference between shims and polyfills is that shims only mimic the feature but has its own, proprietary API. Polyfills emulate both the feature and the exact API of the HTML5 feature it substitutes. So generally speaking, using a polyfill saves you the hassle of having to adopt a proprietary API.

The HTML5 Cross Browser Polyfills collection on github contains a growing list of available shims and polyfills.

Modernizr, for example, includes the “HTML5Shim” for semantic tag support, but it is easy to load other shims/polyfills if Modernizr detects that a feature is not supported.

 

“Won’t adding all these script libraries make my page huge and slow to load?”

Glad you asked! It’s true that adding many of these supporting libraries can add substantial overhead to your site. That’s why you shouldn’t go overboard—load them dynamically when required. With a shim or polyfill, the best practice is to only load them when you have detected that the browser really needs it because the native HTML5 feature is not supported.

 

Again, you’re in luck! There is a great library that supports exactly this scenario: yepnope.js

It’s an asynchronous “resource loader” that works with both JavaScript and CSS and fully decouples preloading from execution. This means that you have full control of when your resource is executed, and you can change that order on the fly. Yepnope will be integrated in Modernizr 2, but can also be used on its own.

Let’s have a look at yepnope’s syntax:

  <script type=”text/javascript” src=”yepnope.1.0.2-min.js”></script>

 

  <script type=”text/javascript”>

      yepnope({

          test : Modernizr.geolocation,

          yep  : ‘normal.js’,

          nope : [‘polyfill.js’, ‘wrapper.js’]

      });

  </script>

 

This example will test the browser’s capability of using HTML5 geolocation using Modernizr. If supported, your own code (normal.js) will be loaded, otherwise a custom polyfill (that consists of polyfill.js and wrapper.js) will be loaded.

 

Summary: Dos and Don’ts

Let’s finish by summarizing the most important points:

 

DO

DON’T

Browser Detection

Try to avoid altogether

OR

Test for a specific browser AND version

Make assumptions for future browsers by only testing for the browser name.

Feature Detection

Test for standards first

Assume unrelated features under one test.

 

Learn more about browser and feature detection at:

 

MSDN Articles:

 

JavaScript Libraries:

 

Polyfills / Shims:

 

 

About the author

 

Sascha P. Corti works for Microsoft Switzerland as a Developer Evangelist focusing on the Microsoft system platform and developer tools and introducing the latest trends to the Swiss developer community. He is currently focusing on web technologies and the Windows Phone 7 platform.

 

His studies involved Computer Science at ETH Zurich and Information Management at the University of Zurich.

His work experience includes seven years as a software developer and architect for a major Swiss bank and working as a systems engineer and technology specialist for several American hi-tech companies, including Silicon Graphics and Microsoft.

You can read about his latest activities on Sascha’s weblog at: http://techpreacher.corti.com or follow him on Twitter @TechPreacher.

 

 

This article was reprinted with permission from Microsoft Corporation. This site does business with Microsoft Corporation.

Popular Articles

Featured