Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.


  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • DownloadDownload
  • PrintPrint
Share this Page URL
Help

4. The Desktop Web > Device and Feature Detection

Device and Feature Detection

The first step in delivering new HTML5-based features to your user base is actually detecting what the browser supports. You need to communicate with the browser to see what it supports before the page is even rendered. In the past, you were forced to detect and parse the—sometimes unreliable—browser userAgent (UA) string and then assume you had the correct device. Today, such frameworks as Modernizr.js or just simple JavaScript, help you detect client-side capabilities at a much more finely grained level.

Which detection method is best? The feature-detection approach is new, but growing, while the approach of parsing the userAgent string is flawed and should be handled with caution. Even at the time of this writing, browser vendors are still getting it wrong. The userAgent string for the latest phone-based Firefox on Android, for example, reports itself as a tablet not a phone. Mozilla uses the exact same userAgent string for both phones and tablets, and that string has the word “Android” in both. The key to success is understanding how you can use each approach most effectively, either by itself or to complement the other.

Client-Side Feature Detection

JavaScript-based feature detection is often implemented by creating a DOM element to see if it exists or behaves as expected, for example:

detectCanvas() ? showGraph() :
showTable();

function detectCanvas() {
  var canvas = document.createElement("canvas");
  return canvas.getContext ? true : false;
}

This snippet creates a canvas element and checks to see if it supports the getContext property. Checking a property of the created element is a must, because browsers will allow you to create any element in the DOM, whether it’s supported or not.

This approach is one of many, and today we have open source, community-backed frameworks that do the heavy lifting for us. Here’s the same code as above, implemented with the Modernizr framework:

Modernizr.canvas ? showGraph()
: showTable();

Feature-detection frameworks may come at a cost, however. Suppose you are running a series of tests on the browser window before the page is rendered. This can get expensive: running the full suite of Modernizr detections, for example, can take more than 30 milliseconds to run per page load. You must consider the costs of computing values before DOM render and then modifying the DOM based on the framework’s findings. When you’re ready to take your app to production, make sure you’re not using the development version of your chosen feature detection library.

On the plus side, frameworks like Modernizr provide a build tool that enables you to pick and choose the features your app must have (Figure 4-2). You can select exactly which features you want to detect, and thereby reduce the overall detection footprint in a production environment.

Feature-detection performance also depends on the devices and browsers you are targeting. For example, running a feature-detection framework on a first-generation smartphone or old BlackBerry could crash the browser and cause your app to fail completely. Take the time to tweak feature detection to gain top performance on your target browsers.

Modernizr production configuration choices

Figure 4-2. Modernizr production configuration choices

Sometimes, as well, you may need to go a step further and detect the actual form factor of the device. FormFactor.js can help you with this. It helps you customize your web app for different form factors (a mobile version, a TV version, and the like). For example:

if(formfactor.is("tv")) {
  alert("Look ma, Im on tv!");
}

if(formfactor.isnt("tv")) {
  alert("The revolution will not be televised");
}

Because FormFactor.js is a framework to manage conceptually distinct user interfaces, it doesn’t eliminate the need for feature detection. It does, however, help you to use feature detection in the context of a particular form factor’s interface.

Although the community has gone a bit inactive lately, you can find more examples at https://github.com/PaulKinlan/formfactor.

Client-Side userAgent Detection

There are times when you must detect the userAgent and parse it accordingly. Typically, you can determine the browser by inspecting JavaScript’s window.navigator object or by using the userAgent request header on the server side. This approach may work for most browsers, but it’s not dependable, as noted in a recent bug report for the MobileESP project:

Issue Summary: When using the Firefox browser on an Android mobile phone, the MobileESP code library erroneously reports the device as an Android tablet. An Android tablet is correctly identified as an Android tablet. This issue only affects mobile phones and similar small-screen Android devices like MP3 players (such as the Samsung Galaxy Player).

Root Cause: Mozilla uses the exact same userAgent string for both phones and tablets. The string has the word ‘Android’ in both. According to Google guidelines, Mozilla should include the word ‘mobile’ in the userAgent string for mobile phones. Unfortunately, Mozilla is not compliant with Google’s guidelines. The omission of the word ‘mobile’ is the reason why phones are erroneously identified as tablets.

So if userAgent detection isn’t always dependable, when is it a good choice to use?

  • When you know, ahead of time, which platforms you are supporting and their UA strings report correctly. For example, if you care about only the environment (not its features) your application is running in, such as iOS, you could deliver a custom UI for that environment only.

  • When you use it in combination with feature-detection JavaScript that calls only the minimum functions needed to check the device. For example, you may not care about the discrepancy in the reported string, because it’s unneeded information. You might only care that it reports TV, and everything else is irrelevant. This also allows for “light” feature detection via JavaScript.

  • When you don’t want all JavaScript-based feature tests to be downloaded to every browser and executed when optimizations based on userAgent-sniffing are available.

Yahoo! has its own reasons for using userAgent detection:

At Yahoo we have a database full of around 10,000 mobile devices. Because userAgent strings vary even on one device (because of locale, vendor, versioning, etc.), this has resulted in well over a half a million user agents. It’s become pretty crazy to maintain, but is necessary because there’s really no alternative for all these feature phones, which can’t even run JavaScript.

On the other hand, Google and other companies opt for a JavaScript-based (also ported to node.js) userAgent parser internally. It’s a wrapper for an approximately 7Kb JSON file, which can be used in other languages. You can find more information at https://github.com/Tobie/Ua-parser, but here is a snippet:

var uaParser =
require('ua-parser');
var ua = uaParser.parse(navigator.userAgent);

console.log(ua.tostring());
// -> "Safari 5.0.1"

console.log(ua.toVersionString());
// -> "5.0.1"

console.log(ua.toFullString());
// -> "Safari 5.0.1/Mac OS X"

console.log(ua.family);
// -> "Safari"

console.log(ua.major);
// -> 5

console.log(ua.minor);
// -> 0

console.log(ua.patch);
// -> 1

console.log(ua.os);
// -> Mac OS X

Another platform detection library written in JavaScript is Platform.js. It’s used by jsperf.com for userAgent detection. Platform.js has been tested in at least Adobe AIR 2.6, Chrome 5–15, Firefox 1.5–8, IE 6–10, Opera 9.25–11.52, Safari 2–5.1.1, Node.js 0.4.8–0.6.1, Narwhal 0.3.2, RingoJS 0.7–0.8, and Rhino 1.7RC3. (For more information, see https://github.com/Bestiejs/Platform.js.)

The following example shows the results returned from various browsers when using Platform.js:

// on IE10 x86 platform
preview running in IE7 compatibility mode on
// Windows 7 64 bit edition
platform.name; // 'IE'
platform.version; // '10.0'
platform.layout; // 'Trident'
platform.os; // 'Windows Server 2008 R2 / 7 x64'
platform.description; // 'IE 10.0 x86 (platform preview; running in IE 7 mode) on Windows
Server 2008 R2 / 7 x64'

// or on an iPad
platform.name; // 'Safari'
platform.version; // '5.1'
platform.product; // 'iPad'
platform.manufacturer; // 'Apple'
platform.layout; // 'WebKit'
platform.os; // 'iOS 5.0'
platform.description; // 'Safari 5.1 on Apple iPad (iOS 5.0)'

// or parsing a given UA string
var info = platform.parse('Mozilla/5.0 (Macintosh; 
                                     Intel Mac OS X 10.7.2; en; rv:2.0)
Gecko/20100101 Firefox/4.0 Opera 11.52');
info.name; // 'Opera'
info.version; // '11.52'
info.layout; // 'Presto'
info.os; // 'Mac OS X 10.7.2'
info.description; // 'Opera 11.52 (identifying as Firefox 4.0) on Mac OS X
10.7.2'

 

Server-Side userAgent Detection

On the server side, MobileESP is an open source framework, which detects the userAgent header. This gives you the ability to direct the user to the appropriate page and allows other developers to code to supported device features.

MobileESP is available in six languages:

  • PHP

  • Java (server side)

  • ASP.NET (C#)

  • JavaScript

  • Ruby

  • Classic ASP (VBscript)

In Java, you would use:

userAgentStr =
request.getHeader("user-agent");
httpAccept = request.getHeader("Accept");
uAgentTest = new UAgentInfo(userAgentStr, httpAccept);

If(uAgentTest.detectTierIphone()){
...//Perform redirect
}

In PHP, the code would be:

<?php

      //Load the Mobile Detection library
      include("code/mdetect.php");

      //In this simple example, we'll store the alternate home page
      // file names.
      $iphoneTierHomePage = 'index-tier-iphone.htm';

      //Instantiate the object to do our testing with.
      $uagent_obj = new uagent_info();

      //In this simple example, we simply re-route depending on
      // which type of device it is.
      //Before we can call the function, we have to define it.
      function AutoRedirectToProperHomePage()
      {
          global $uagent_obj, $iphoneTierHomePage,
               $genericMobileDeviceHomePage, $desktopHomePage;

          if ($uagent_obj->isTierIphone == $uagent_obj->true)
          //Perform redirect
      }

So there you have it, userAgent detection is unreliable and should be used with caution or for specific cases only. Even in the scenario descripted by the Android/Firefox bug report, for example, you could still implement userAgent detection and then use feature detection to find the maximum screen size for Android-based mobile phones using CSS Media Queries. There’s always a workaround, and problems such as these should not deter you from using the userAgent string.