Skip to main content Accessibility Feedback

Writing your own simple feature tests

Progressive enhancement is an approach to web development in which your provide universal access to content to all devices, and layer in additional features and functionality for browsers that support them.

For this approach, you need to run a simple feature test to check what the browser is and isn’t capable of. Modernizr is a great tool for this, but today, I want to show you how you can write your own simple feature tests.

What a feature test looks like #

At it’s core, a feature test is a simple if statement:

if ( support for feature exists ) {
    // Do something

I use feature tests in two ways:

  1. I’ll add classes to the document to activate certain styles in my CSS file.
  2. I’ll wrap them around my JavaScript to ensure the browser can handle the API’s used.

Let’s look at some examples.

Checking for modern JavaScript support #

I use modern JavaScript API’s in my scripts that don’t work in older and less capable browsers. These API’s include query selectors, event listeners, and foreach loops. Here’s the if statement I use:

if ( 'querySelector' in document && 'addEventListener' in window && Array.prototype.forEach ) {
    // Do stuff...

Some of my scripts use CSS to hide and show elements. By default, I want all elements visible. They should only be hidden if the browser has the appropriate JavaScript support to make them visible. To do that, I include this script:

if ( 'querySelector' in document && 'addEventListener' in window && Array.prototype.forEach ) {
    document.documentElement.className += 'js';

That adds a .js class to the <html> element. In my CSS, I’ll include something like this:

.js .example {
    display: none;
    visibility: hidden;

Now, elements with the .example class are only hidden when the appropriate API’s are supported.

I also don’t want to run scripts that use unsupported API’s, as that can result in errors and weird layout quirks. I’ll wrap a feature test around my script like this:

if ( 'querySelector' in document && 'addEventListener' in window && Array.prototype.forEach ) {
    // My scripts go here...

Checking for @font-face support

I love using icon fonts, but certain browsers (notably Opera Mini and IE 9 on Windows Phone 7) don’t support font embedding. I also use the :before pseudo selector to include icons in my HTML.

For browsers that don’t support either of those, users will see empty squares where the icons should be. I’d rather they see nothing at all, or in some cases, see text instead. Paul Irish and Diego Perini shared two little functions that check for support of these features.

I include these functions in my JS file, and then use this if statement:

if (isFontFaceSupported && selectorSupported(':before')) {
    document.documentElement.className += 'font-face';

Then in my stylesheet, I make sure that I prefix my @font-face styles with .font-face.

Including a feature test on your site #

So you’ve written your feature test. Now where do you put it?

Tests that I wrap around scripts go in my main JavaScript file. Feature tests that add a class to the document go in their own file that I usually name feature-test.js. While most of my JS files go in the footer for better performance, I want the test to run as quickly as possible to avoid “flashes of unstyled content” (or FOUC, as it’s sometimes called).

I include the feature-test.js in the <head> element:

<script src="feature-test.js"></script>

How do you know what to check for? #

Google is a great place to start. A lot of the conditional statements can be found on the Mozilla Developer Network, where they’re included as part of the polyfill recommendations. And Paul Irish, who’s a jQuery team member and lead developer at Modernizr, shares a lot of the code they use on GitHub.

🚀 I just relaunched my Vanilla JS Pocket Guides with new code examples and real projects to help tie everything you’ll learn together. Check it out.

Have any questions or comments about this post? Email me at or contact me on Twitter at @ChrisFerdinandi.

Get Daily Developer Tips