Lazy feature testing

The problem

I’ve been thinking about Modernizer. It tests for feature detection. Currently 44 tests, but maybe more to go. If it was going further by testing more features and why not feature conformance, then, when inserting it, you’d be executing hundreds if not thousands of tests at the startup of your page.

Do you need all this information? Do you need all the tests to be executed? You probably don’t. You probably just need a couple of tests and so, this may be a useless burden added to your initial page loading.

I’ve consequently tried to create a lazy feature testing mecanism. The point is to only execute the test functions when you need them.

The solution

As a reduction of the problem, I will only consider that Modernizer create an object with properties named as features and with boolean values (I purposefully forget the whole html class attribute situation. I’ll mention them at the end).

So, the solution is: getters!
Based on a name and a test function, a property is defined as a getter. When called for the first time, the getter executes the test function, and replaces the accessor property by a data property which value is the result of the test. Hence, the next time the property is called, the value is already there. It is completely transparent to the user which has no idea that once the property called a function and the other times just retrieves a value.

The clean and standard way to do this is to use ECMAScript 5 Object.defineProperty().

Modernizer.addTest = function(name, test){
    Object.defineProperty(this, name, {get: function(){
                                                var b = !!(test());
                                                Object.defineProperty(this, name, {value:b});
                                                return b;  
                                             },
                                       configurable:true});
};

Wait a minute. There is an Object.defineProperty() in IE8

Yes, there is and it only works on DOM objects. Moreover, there are some property attribute combinaisons which aren’t quite allowed in IE8. More than that, apparently, it is not possible to redefine a property from an accessor to a data property. Hence I had to delete the existing property to be able to redefine it.

Without Object.defineProperty()

Obviously, other browers don’t support Object.defineProperty(). The solution resides in __defineGetter__ if it exists and giving up (not doing lazy testing and executing everything) if it doesn’t.

Bonus: get operator

There is another way to define getters in JavaScript which is using the get operator.

var o = {get myfeature(){return true;}};

However, as far as I can tell, all browsers allowing this allow one of the previous solutions, so, there is no need for that. But let’s assume there is for a minute. So the Object.defineProperty doesn’t work, the __defineGetter__ doesn’t either. Does the get operator work?

How to perform JavaScript syntax feature detection?

This is actually an interesting question. Usually, feature detection uses JavaScript to see if there is such object or such function or executing such function. Here, I don’t need to execute JavaScript. I need to see if some JavaScript syntax is supported. Consequently, I “cannot use JavaScript itself”, because if I do and if the syntax isn’t supported in the tested browser, the interpreter will stop. The only solution I can think of is actually to put the JavaScript code in eval(), wrap it in a try-catch block and see if a SyntaxError is thrown.

But when you’ve “proved” that your syntax is working, how do you use it without breaking in browsers that don’t support this syntax? I see two solutions. One is to dynamically and conditionnally loading a script with this syntax if proved that it works. The other solution would be to use eval, again.

For the record, nothing in the bonus section has been tested. These are just thoughts.

Applying it to Modernizer

Obviously, with how Modernizer currently works, my strategy cannot be used since modernizer adds classes to the html element. In order to apply my technique Modernizer would need to be changed so that a Modernizer user would explicitely say what tests they want to perform.

Conclusion

We have seen in this article getters used in order to perform lazy feature detection. We’ve also discussed how JavaScript syntax feature detection could be performed. Check out the code to test and mess around. It has been successfully testing on IE8, IE7 (actually IE8 in IE7 mode), latest stable Chrome, Firefox 3.6 and Firefox 4b10.

Edit: Two very interesting articles ([1] [2]) made me realize that only old browsers do not have JavaScript getters and features/quirks/problems of these are well-known. Based on that assumption, for these old browser, having a constant object with all test results pre-computed could be a complementary solution to lazy feature testing with getters.

About these ads

One thought on “Lazy feature testing

  1. Yeah lazy definitely has an edge..

    Though when you use Modernizr’s html classes you want those in place immediately. So you want lazy for the javascript based ones…

    I had been thinking about implementing lazy for the Modernizr.* checks if getters are supported.. but never got around to it.

    has.js basically tackles this sort of thing straight on. It’s api is designed so you can do lazy tests. which works!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s