Update 8: Feature Detection

Fabricio C Zuardi edited this page Sep 15, 2013 · 10 revisions

(to read all the updates and an intro about me go to the wiki's homepage to check out the actual progress of the code go to the showdown_counter git repository)

So, I wanted to use some hardware feature from the device, and as stated on my update zero and on the intro, I am also doing this app as a test of how much the HTML5 promise is for real.

There are a whole lot of device APIs being discussed and proposed to be part of the HTML5 standard on W3C, this WebAPI page on MDN lists a bunch of them, and this page on the Mozilla Wiki has a very nice table with the links for the recomendations, drafts and also which ones currently works on their mobile platforms as well (Firefox Mobile for Android and Firefox OS). Other than the specs being proposed to W3C, there is also the Apache Cordova APIs that is another way that web developers so far have used on their webapps-made-to-be-encapsulated-and-run-on-a-specific-target-platform… and that's great since like the project name says (Phone Gap) those APIs and the software to wrap this webapp inside a WebView of a native software (in Java or Objective C, or QT/QML, whatever) is there to fill a gap on the HTML standards itself.

But we shouldn't ignore the standard, we must work together to push W3C standards forward. Yes, use the Cordova APIs if needed, Ubuntu Touch has support for them (or so the documentation say), but participate on the discussions to make such APIs a reality for any webpage / webapp as well.

In the Counters app that I am developing, I want to use at least one of those APIs, I've chosen the Vibration API to start. I want to give the user a tactile feedback if she tries to decrease the counter level less than the current bottom limit (which is zero).

If I didn't had to worry about compatibility and user agents that haven't implemented the feature yet, I would do something simple like this (on my counter.js class):

        this.setValue = function (v) {
            if ((v <= bottomLimit) || (v >= topLimit)) {
            value = Math.min(Math.max(v, bottomLimit), topLimit);
            domElement.dataset.value = value;
            displayElement.textContent = value;

While this works as expected on Firefox Mobile, a call for window.navigator.vibrate(50); on a browser/system that don't have support for this feature will give you an error and break your app. Here is what Google Chrome gives you:

Uncaught TypeError: Object #<Navigator> has no method 'vibrate'

And I suspect that Ubuntu Touch, using a Webkit-based engine would also fail and make the app unusable (I've test it on qmlscene, and it does).

So what should we do? Detect the system where my app is running based on the User Agent string and serve to the user a different version of my app? No! Don't do that

Please use feature detection

If the evolution of web pages taught us something, is that we should always try to detect features based on feature availability. Not based on the browser/system detection.

Here is one way of doing it:

            if ((v <= bottomLimit) || (v >= topLimit)){
                if (window.navigator &&
                    window.navigator.vibrate &&
                    typeof window.navigator.vibrate === 'function'){

That is better, and will not throw us an error on systems where navigator.vibrate is undefined or not a function.


If you like to use cutting-edge features, and don't want to write feature detection code, one option is to use libraries such as Modernizr, then once you are using it you can simply check with:

            if ((v <= bottomLimit) || (v >= topLimit)) {
                if (Modernizr.vibrate) {

Modernizr v3.0 with Grunt and Requirejs

My build system is based on Grunt, and I am using Requirejs to keep my code organized, and I am also using r.js to optimize and concatenate everything afterwards. So the natural thing to do would be to look for a grunt-modernizr plugin.

There is one, and there is a branch with a better config and output, to install it I've used the instructions of this issue comment:

npm install grunt-modernizr@https://github.com/Modernizr/grunt-modernizr/tarball/feature/modernizr-3.0 --save-dev

And the README at https://github.com/Modernizr/grunt-modernizr/tree/feature/modernizr-3.0

The nice thing about using Modernizr as a Grunt plugin is that it will crawl your code looking for features that you've used (or detected with the global variable Modernizr), and auto-generate a custom lean version of the library containing only the feature-detection tests you need!

Cordova Vibrate

The only problem, is that the Modernizr feature-detection for vibration — as of this writing — detects only the W3C Candidate Recommendation way of doing it, and not the Apache Cordova API way of doing vibrate. Which is:

// Vibrate for 2.5 seconds

And Ubuntu SDK / QML / Webkit / Ubuntu Touch device, will probably understand that version, so I will have to write a simple polyfill to make everyone happy and hopefully working…


Developing for the web is not easy :)