JavaScript Feature Detection with has.js

Dojo Toolkit Project Lead Peter Higgins has been working on an exciting new project called has.js. Higgins describes this project best:

Browser sniffing and feature inference are flawed techniques for detecting browser support in client side JavaScript. The goal of has.js is to provide a collection of self-contained tests and unified framework around using pure feature detection for whatever library consumes it.

Simply put, has.js tests the browser environment to discover if the browser supports any given feature. has.js includes a growing number of tests, ranging in many categories, including:

EcmaScript 5 features (Object.freeze, Array.map, etc.)

CSS features (rgba, border radius, etc.)

HTML5 and advanced JavaScript APIs (classList, placeholder attribute, etc.)

Script loading features (defer, async)

Native JSON, Audio, Video support

XHR support

...and more!

Let's explore how to use has.js, its modular test collections, and create custom feature detection tests.

has.js Usage

has.js uses a global has function which you pass a string to test against. If we wanted to test the presence of a natively supported Array.forEach method, we would code:

// If Array.forEach is not present... if(!has("array-foreach")) { // ... create it! Array.prototype.forEach = function() { // .... }; }

The string passed to the has function represents the test's key as defined when the test was created. What's a real test creation look like? Let's review one!

has.js Test Creation

The code behind the Array.forEach test is short and sweet:

(function(has, addtest, cssprop){ var toString = {}.toString, EMPTY_ARRAY = [], FUNCTION_CLASS = "[object Function]"; addtest("array-foreach", function(global, document, anElement){ return toString.call(EMPTY_ARRAY.forEach) == FUNCTION_CLASS; }); })(has, has.add, has.cssprop);

has.js also provides an ES5 Array check which includes other has tests:

(function(has, addtest, cssprop){ addtest("array-es5", function(){ return has("array-every") && has("array-filter") && has("array-foreach") && has("array-indexof") && has("array-isarray") && has("array-lastindexof") && has("array-map") && has("array-reduce") && has("array-reduceright") && has("array-some"); }); })(has, has.add, has.cssprop);

Simple enough to create tests, right? Let's create a few of our own!

Custom has.js Test Creation

As you hopefully noticed in the tests above, the test itself is actually a function that returns true if the browser supports a given feature or false if the browser does not. Let's create a test that tells us if the browser supports RGBA.

addtest("css-rgba", function(g, d, el){ var re = /^rgba/, supported = null; if(has("css-enabled")){ try{ el.style.color = "rgba(1,1,1,0.5)"; supported = re.test(el.style.color); el.style.color = ""; }catch(e){} } return supported; });

A test may also return null if a test is not applicable to the current browser. For example, browsers other than IE will return null for ActiveX, as ActiveX is an Microsoft-only technology.

Creating has.js modules specific to your project may be the best option if your web application requires many different features. These abstractions may allow you to code your application faster.

has.js is Growing!

has.js is still in its infancy but the utility clearly has a bright future. The beauty of has.js is that its functionality is extremely useful but the tests themselves are very simple to create. If you have ideas for more tests or simply improvements to has.js, feel free to fork the project and send pull requests to the main repo.