JavaScript Feature Detection with has.js

By on  

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,
		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, 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;

          el.style.color = "rgba(1,1,1,0.5)";
          supported = re.test(el.style.color);
          el.style.color = "";
    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.

Track.js Error Reporting

Upcoming Events

Recent Features

  • Regular Expressions for the Rest of Us

    Sooner or later you’ll run across a regular expression. With their cryptic syntax, confusing documentation and massive learning curve, most developers settle for copying and pasting them from StackOverflow and hoping they work. But what if you could decode regular expressions and harness their power? In...

  • From Webcam to Animated GIF: the Secret Behind chat.meatspac.es!

    My team mate Edna Piranha is not only an awesome hacker; she's also a fantastic philosopher! Communication and online interactions is a subject that has kept her mind busy for a long time, and it has also resulted in a bunch of interesting experimental projects...

Incredible Demos

  • Rotate Elements with CSS Transformations

    I've gone on a million rants about the lack of progress with CSS and how I'm happy that both JavaScript and browser-specific CSS have tried to push web design forward. One of those browser-specific CSS properties we love is CSS transformations. CSS transformations...

  • Introducing MooTools Dotter

    It's best practice to provide an indicator of some sort when performing an AJAX request or processing that takes place in the background. Since the dawn of AJAX, we've been using colorful spinners and imagery as indicators. While I enjoy those images, I am...


  1. Stupid question:
    Why does everyone compare the toString() value of an object to test whether it is a function? I’m referring to the code behind the Array.forEach test.

    Why not obj.constructor === Function?

  2. Duncan Krebbers

    How is this compared with modernizr? It seems a bit equal to me.

    • I want to say that modernizr is more targeted toward CSS. I see modernizr is adding CSS class and such. has.js simply provides a true or falsey value.

    • The ultimate goal with has.js is to embed it in other libraries by way of a customized “builder”, including only tests you want. The hope is that Modernizr would simply be a specific export of various tests, with Modernizr adding in the functionality to do things like add class names to the element etc.

      Paul irish, of Modernizr, is also on the has.js team. The tests are mostly identical between the two. Only the API surface differs.

    • Paul Irish, a maintainer of Modernizr, is also a contributor to has.js. Besides adding the className’s to the documentElement, Modernizr also executes all tests at once, where has.js allows for deferred testing (test when u need it). Modernizr also applies the “HTML5 shim” to allow HTML5 elements to be styleable in older IE.

      There are also differences in what each project considers supported. For example Modernizr will report that input type=range is supported in browsers like Safari 3 despite lacking much of the required HTML5 API.

  3. I’ve been playing around with the Modernizr script which does all testing at once, as mentioned above. I assume you could do the same with has.js? Is there a demo page with running code some where. I’m not sure of what is the intended ‘best way’ to call individual or all test of browser capabilities thru has.js. Is it Dojo like, in that you call individual test modules as needed?

  4. Follow-up: I download the has.js bundle and found the test page at ‘/tests/runTests.html’. Very nice collection of tests. Wish I had this sooner. I would prefer a Dojo style of loading files with something like has.require(‘video’) and so forth. And I’m still a little confused, as I’m still not sure what variable to access for what category of test(s): ie some sort of Dojo style naming convention like ‘has.video.test-name’ might be nice. But the project is still new and from your description, they want to remain ‘generic’, so I will dig around some more for insight.
    Thanks for the post.

  5. AprilMorone (aka 'linuxwebdeveloper')

    When I’d had the HTML and Dynamic HTML class as a part of my AAS degree as a Computer Support Specialist around the same time as I was doing Web Dev stuff, I’d been required as part of one of my class projects to use modernizr. I agree with David Walsh as to it’s effects and to what it affects because I got to see, first hand, what it (modernizr) does. I think using has.js could be a better choice.

  6. I’m referring to the code behind the Array.ie some sort of Dojo style naming convention like has.video.test-name might be nice

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • OâReilly Velocity Conference â Amsterdam

    My favorite front-end conference has always been O'Reilly's Velocity Conference because the conference series has focused on one of the most undervalued parts of client side coding:  speed.  So often we're so excited that our JavaScript works that we forget that speed, efficiency, and performance are just as important. The next Velocity...

  • CanIUse Command Line

    Every front-end developer should be well acquainted with CanIUse, the website that lets you view browser support for browser features.  When people criticize my blog posts for not detailing browser support for features within the post, I tell them to check CanIUse:  always up to date, unlike...

  • Generating Alternative Stylesheets for Browsers Without @media

    If your CSS code is built with a mobile-first approach, it probably contains all the rules that make up the "desktop" view inside @media statements. That's great, but browsers that don't support media queries (IE 8 and below) will simply ignore them, ending up getting the...

  • Serve a Directory with PHP

    Many developers have a giggle at PHP, even looking down at the language, but let's be honest:  most of our blogs are powered by it (WordPress) and it's a great language to dabble around with.  I cut my teeth on PHP, though I prefer to avoid PHP these days. But...

  • Tips you can Use to Build an Excellent eCommerce Experience

    There are many reasons to build a good eCommerce experience if you are planning to build an eCommerce website. Visitors can place items in their cart and leave your site without making a purchase. In fact, three out of every four of them will do so....