Combining Modernizr tests to create custom ‘convenience’ forks
Have a situation where you need to fork code based upon the capability of a device? This is the bread and butter of Modernizr.js. However, what about if you want to fork code (either JS or CSS) based upon two or more tests passing, not just one?
Personally I do this when I want to facilitate progressive enhancement of features/design on more than just a single test. Put another way, one could say, “The device needs to be able to do this and support that before it gets the ‘enhanced’ experience”.
By combining multiple tests it’s possible to create a very specific fork for your own needs to partition experience ‘tiers’. As a simple example, a ‘base’ experience (for less capable devices) and an ‘enhanced’ counterpart (for more capable ones). The combination of tests provide a simple (and personal to you) switch from the base experience to the enhanced experience.
This post explains how you can use the Modernizr ‘addTest’ API to create your own ‘feature’ (or cheat and combine two existing ones to make a new custom test) test and create a custom Modernizr build that contains it.
I’m a JS newbie so hopefully if you’re a beginner like me, this little ‘sing-a-long’ will help.
The result
The result will be the relevant class name in your html
classes depending upon whether or not the test passes:
<html class="mynewtest …">
Or:
<html class="no-mynewtest …">
With that you can easily fork your CSS. However, this will also allow you to fork on the property in your JavaScript. For example:
if (!Modernizr.mynewtest) {
// Stuff
}
Let’s do this
OK, we know what we’re getting so let’s do this.
Clone Modernizr
First of all we will need to download the Modernizr 3 code. Easiest way to do this is to open Terminal, browse to the folder we want the Modernizr project to live in (for example the root of my Sites
folder and run:
sudo git clone https://github.com/Modernizr/Modernizr.git
Install Modernizr dependencies
Now we have a Sites/Modernizr folder with the latest Modernizr project in. Still on the Terminal, move into the Modernizr folder and install the dependencies it requires:
cd Modernizr;sudo npm install
A whole bunch of WarGames code will stream up the screen as the Modernizr dependencies are downloaded. Let it do its thing.
Test the Modernizr build task
Once the dependencies have finished downloading (e.g. the command line prompt has a blinking cursor ready to roll), test the existing Modernizr build task actually works by typing:
grunt build
This will build a version of Modernizr into the Modernizr/dist
folder. There should be a file called something like modernizr-build.js
in there. Hopefully, that just happened and all is well? Great. Let’s continue.
Remove tests that aren’t needed
The version we just built is a version that contains all the Modernizr tests. It’s likely we don’t want all the tests (if you do – what the hell for??) so open Modernizr/lib/config-all.json
and remove the lines of all the tests that aren’t needed (Pro tip: it’s a JSON object so you can’t comment out the tests you don’t want – make a backup of the file first in case you mess up).
Write a reference to the custom test
At the end of the aforementioned config-all.json
file, add a reference for the new custom test:
"test/mynewtest"
Dan Donald also suggested prefixing custom tests/features with ‘feature-‘ to make them more explicit. Something worth considering.
Right, all we need to do now is write our custom test.
Writing our own custom test (or combining existing ones)
Thankfully the hard work of writing and understanding the syntax of a Modernizr test has been done already. Just open one of the existing tests (they are all stored as separate little JS files inside the ‘feature-detects’ folder) and amend it and save it back into the feature-detects
folder with a new name. Here is the example contents of a new custom test Modernizr/feature-detects/mynewtest.js
:
/*!
{
"name": "My New test",
"notes": [
{
"name": "Test the kind of device with Blob and SMIL support combined",
"href": "htp://modernizr.com"
}
],
"property": "mynewtest",
"tags": ["smil", "blob"]
}
!*/
define(['Modernizr'], function ( Modernizr ) {
Modernizr.addTest('mynewtest', function() {
try {
new Blob(); // this line can throw an exception.
return !!document.createElementNS && /SVGAnimate/.test( toStringFn.call( document.createElementNS('http://www.w3.org/2000/svg', 'animate') ) );
} catch(e) {
return false;
}
}
);
});
Here we are combining two existing Modernizr tests to be a new custom test; Blob support and SMIL animation support. In this instance we only want the test to pass if the device in question supports both those capabilities.
try
and catch
before so I was unsure of the syntax for testing multiple ‘things’. However, good ol` Stack Overflow provided me the answer.
If using this format of test, the key point is that the first test within the try
doesn’t have the double exclamation (as it does in the original Blob support test). If that double exclamation was left in place it would convert the result to a boolean and therefore ‘returns true’ early (before the SMIL test runs).
Great, that wasn’t too bad right? Those super smart Modernizr folks have provided a relatively simple way to extend Modernizr to our whim. Let’s just build the sucker:
Build the new Modernizr file
Back on the Terminal, run this command again:
grunt build
Grunt will now build the new custom Modernizr file, complete with our new test. Drop that into your project and you’ll now have your entirely personal test on which to fork your code.
Leave a Reply