Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.
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.
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();functiondetectCanvas(){varcanvas=document.createElement("canvas");returncanvas.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.
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.
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
userAgentstring for both phones and tablets. The string has the word ‘Android’ in both. According to Google guidelines, Mozilla should include the word ‘mobile’ in theuserAgentstring 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
userAgentstrings 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:
varuaParser=require('ua-parser');varua=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);// -> 5console.log(ua.minor);// -> 0console.log(ua.patch);// -> 1console.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 platformpreviewrunninginIE7compatibilitymodeon// Windows 7 64 bit editionplatform.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 WindowsServer2008R2/7x64'// or on an iPadplatform.name; // 'Safari'platform.version; // '5.1'platform.product; // 'iPad'platform.manufacturer; // 'Apple'platform.layout; // 'WebKit'platform.os; // 'iOS5.0'platform.description; // 'Safari5.1onAppleiPad(iOS5.0)'// or parsing a given UA stringvar info = platform.parse('Mozilla/5.0(Macintosh;IntelMacOSX10.7.2;en;rv:2.0)Gecko/20100101Firefox/4.0Opera11.52');info.name; // 'Opera'info.version; // '11.52'info.layout; // 'Presto'info.os; // 'MacOSX10.7.2'info.description; // 'Opera11.52(identifyingasFirefox4.0)onMacOSX10.7.2'
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=newUAgentInfo(userAgentStr,httpAccept);If(uAgentTest.detectTierIphone()){...//Perform redirect}
In PHP, the code would be:
<?php//Load the Mobile Detection libraryinclude("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=newuagent_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.functionAutoRedirectToProperHomePage(){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.