Home Design Detecting Features With JavaScript

Detecting Features With JavaScript


As new features arrive in browsers, we often need to qualify their use at a more granular level. JavaScript feature detection has long been a part of web development, thanks to proprietary feature differences in early browsers.

Back then and (to a lesser degree) to this day, to get code to work in more than one browser it was necessary to check whether even the most common functions were defined before using them. For example, if we wanted to listen for an event like click, we would first need to check which event API the browser supported:

// if standard event listeners are supported

if( document.addEventListener ){

document.addEventListener( “click”, myCallback, »

false );


// if not, try the Internet Explorer attachEvent method

else if( document.attachEvent ){

document.attachEvent( “onclick”, myCallback );


Detecting JavaScript features

Thankfully, in recent years the web standards movement has nudged browsers into supporting common APIs for features like event handling, which greatly reduces the number of browser specific forks we must apply in our code and makes it more sustainable in the long term. Now it’s more common to use JavaScript feature detection to determine whether a feature is supported, before using that feature to create enhancements on top of an already functional HTML experience. For example, the following JavaScript function detects whether the standard HTML canvas element (a sort of artboard element that offers an API for drawing graphics with JavaScript) is supported:

function canvasSupported() {

var elem = document.createElement(‘canvas’);

return !!(elem.getContext && elem.getContext(‘2d’));


This could be used before loading and running a pile of canvas-dependent code:

if( canvasSupported() ){

// use canvas API safely here!


Detecting CSS features

While detecting features in JavaScript isn’t new, using JavaScript to detect CSS feature support began relatively recently. At the time, new browsers included great new CSS capabilities like float and position, even though browsers with poor support for these features were widely used. This made it difficult to apply modern CSS to a site without breaking the experience for users running older browsers. One example from the article was the following test to see if a browser properly supports the standard CSS box model, which incorporates padding, width, and border into the measured dimensions of an element. At the time, two different box model variations were actively supported across popular browsers, and writing CSS against one model would cause layouts to break in browsers (read: old versions of Internet Explorer) that supported the other.

function boxmodel(){

var newDiv = document.createElement(‘div’);


newDiv.style.width = ’20px’;

newDiv.style.padding = ’10px’;

var divWidth = newDiv.offsetWidth;


return divWidth === 40;


Let’s look at this more closely. The JavaScript function creates a new div element, appends it to the body element in the document, and gives the div some width and padding. The function then returns a statement that the div’s rendered width should equal 40.

Those familiar with the standard CSS box model will recall that the width and padding of an element contribute to its calculated width on the screen, so this function tells you whether the browser calculates that width as expected. In the article, I bundled this test and others for properties like float or position into a suite called enhance.js, which could be run as a broad diagnostic during page load. If the test passed, the script would add a class of enhanced to the HTML element that could be used to qualify the application of advanced CSS properties.

.enhanced .main {

float: left;


Qualifying CSS in this way felt like a sustainable step forward, but enhance.js was admittedly rough around the edges, since it couldn’t detect and apply features at a granular level. Fortunately, developers much smarter than myself picked up the slack and took off running.

Feature detection frameworks

screenshot-modernizr.com 2015-10-31 08-13-07

Almost any modern JavaScript framework uses feature tests within its internal codebase, but one framework stands alone in its mission to provide a standard approach to running tests in our sites: Modernizr, created in 2009 by Paul Irish, Faruk Ateş, Alex Sexton, Ryan Seddon, and Alexander Farkas. Modernizr’s simple workflow of adding specific classes to the html element to signify that a feature like CSS multi-columns is supported (<html class=”…csscolumns…”>) makes the approach accessible to developers not versed in JavaScript detection intricacies, and has become a pseudo-standard approach to qualified application of enhancements.

Using Modernizr

Using Modernizr out of the box is quite straightforward. Include the modernizr.js script in the head of an HTML document, and the script runs feature tests automatically.

<script src=”js/modernizr.js”></script>

When Modernizr tests run, the framework retains a JavaScript property, stored on the globally available Modernizr object, of that test’s name that equals true if it passes or false if it doesn’t.

if( Modernizr.canvas ){

// Canvas is supported!


When a test passes, Modernizr also adds a class of that test’s name to the html element, which you can then use within your CSS selectors to qualify the use of certain features. Quite a lot easier than hand-coding those tests above, right? While you can safely use many modern CSS features without qualification—like box-shadow, border-radius, or transition—relying too heavily on these features can introduce usability issues in browsers that don’t support them. For instance, say you want to overlay text on an image. You want a text color that matches the image and a text shadow to pull the characters forward.

.img-title {

color: #abb8c7;

text-shadow: .1em .1em .3em rgba( 0, 0, 0, .6 );


In browsers without text-shadow support, the text is nearly invisible! To keep this from happening, you may choose to default to a different presentation, perhaps using a color with higher contrast first and then feature detection to enhance to the ideal presentation.

.img-title {

color: #203e5b;


.textshadow .img-title {

color: #abb8c7;

text-shadow: .1em .1em .3em rgba( 0, 0, 0, .6 );


And voilà! You have yourself an accessible experience in browsers new and old.

Detecting CSS support without JavaScript

As useful as JavaScript-driven feature detection is, it comes with the downside of loading and running code for no purpose other than to qualify features we want to use. Ideally, we should standardize the ways we detect features as we do the features themselves; thanks to advocacy from developer Paul Irish, native support for a CSS feature-detection approach has been standardized by the W3C and is gradually becoming available in browsers.

The @supports feature follows a similar syntax to that of media queries. By passing any CSS property and value pair (say, display: flex) to the @supports rule, you can define entire style blocks to apply only in browsers that implement that CSS feature (or features). Here’s an example:

@supports ( display: flex ) {

#content {

display: flex;


…more flexbox styles here


@supports is pretty handy: it offloads feature detection work to the browser, removing the need for us to write custom—and often slow, unreliable—tests to produce similar results. Less work for developers, and better performance for users! In addition to the @supports syntax in CSS, you can pair a JavaScript API called CSS.supports. Here’s an example of it in action, qualifying the use of transition:

if( CSS.supports( “(transition: none)” ) )


// CSS transitions are supported!

// Perhaps you’d add some transition event listeners here…


Support for support

As is the nature of many CSS features, the @supports approach to feature queries will gracefully degrade by itself, meaning you can safely include it in a stylesheet. Browsers that don’t understand @supports will ignore it and the styles it qualifies. We can’t say the same of the JavaScript method that pairs with @supports: funnily enough, before using the CSS.supports JavaScript API, you need to check if the browser supports CSS.supports! If you’ve been developing websites for a while, you’re probably used to this sort of thing. Somewhat amazingly, though, two versions of CSS.supports already exist in the wild because some versions of the Opera browser have a non-standard  implementation (window.supportsCSS). So here’s a snippet that tries to assign a variable css.Supports to one or the other, if available:

var cssSupports = window.CSS && window.CSS.supports || »


With this normalization in place, you can qualify your CSS.supports use as follows:

if( cssSupports && cssSupports( “(transition: none)” »

) ){

// CSS transitions are supported!


Now to play devil’s advocate for a moment: one potential issue with native feature detection like @supports is that it places trust in browsers to report honest results about their own implementation’s standards compliance. For example, the Android 2 browser supports history.pushState—used for changing the browser’s URL location to reflect updates made in the page since last load—but it doesn’t update the actual page address until you refresh the page, making the implementation completely useless. From a web developer’s perspective, any variation from a W3C spec in a browser’s implementation could deem a feature unusable, so where do we draw the line for whether a feature is supported or not?

The spec suggests that support is defined by a browser implementing a particular property and value “with a usable level of support,” which, of course, is subjective. Given that in the past, browser vendors have routinely adjusted their user agent strings to improve their relevance among competitors, there’s also the potential for deliberately dishonest reporting. As for how accurately this detection feature will continue to work, the future remains to be seen.


Your email address will not be published. Required fields are marked *