Optimizing your mootools application using Sizzle!
A little introduction
Some weeks ago I had to improve performance of a really neat mootools based javascript application at work. I'll probably will post later about some common javascript optimizations, however the really *huge* speed boost was to replace mootools' selector machinery with Sizzle.
Sizzle is a CSS selector engine you can use standalone in any application, and you've probably used it before without noticing it: it's the corner stone of jQuery's selectors.
You've probably heard also about the posiblity of replacing mootools selectors before and that improvements are not that relevant. Well, let me disagree with that, I'm not a blind fan, I recognize I like jQuery the most over mootools but I also see things with non-partial eyes. In this case I tried it not because I like jQuery but because I just wanted to get the job done
Sizzlefying mootools
Integrating Sizzle into mootools turned out to be pretty simple, this snippet will do the trick:
Native.implement([Document, Element], {
getElements: function(expression, nocash) {
return new Elements(
new Sizzle(expression, this),
{ddup: (expression.length > 1), cash: !nocash}
);
}
})
Code explanation
The explanation of the code is pretty straightforward, what we are doing here is to replace the getElements method of Document and Element with our Sizzlefied version of it.
The first thing to take care about is to leave the API intact so everything will continue to work (if it was working ;), that means we will respect all arguments, that's why the nocash argument is there:
...
getElements: function(expression, nocash) {
...
Now for the fun part, let me say that the first argument for Sizzle is the selector expression (ie: "input.save") and the second is the context, we are using the javascript keyword this to pass the Document or Element that's calling the function.
...
new Sizzle(expression, this),
...
Sizzle will then return all DOM elements matching the selector, but there's a detail, none of these elements will contain the mootools extensions. That leads us to the problem: if someone tries element.setStyle('display', 'none') will not work, and as we said before, we want to leave the API working as it was, so old code continues doing its magic without new problems :). The solution for this is to use the Elements class. It takes an array of DOM elements and extends them with mootools' magic.
The ddup option indicates if Elements should remove duplicates, and we are setting it to "yes, if we have more than one element in the array".
The cash option is set to !nocash. nocash is normally is undefined and !undefined is true. If the cash option is true, then elements will be extended with mootools' magic (which is what we want)
...
return new Elements(
new Sizzle(expression, this),
{ddup: (expression.length > 1), cash: !nocash}
);
...
And that's it.
Benchmarks (Firefox 3.6)!
Here are some numbers, consider each Transaction as a table row, where each row have a click event handler that turns it into editable (of course, everything is very much complex than that).
# Click after load on a test account with 13 Transactions
Standard Mootools: Time: 1819.709ms, function calls: 348897
Sizzledfied mootools: Time: 208.859ms, function calls: 12832
# Click after checkregister load on a test account with 163 Transactions
Standard Mootools: Time: 7184.338ms, function calls: 1179935
Sizzledfied mootools: Time: 780.623ms, function calls: 18156
# Fresh load on a test account with 335 Transactions
Standard Mootools: Time: 13178.882ms, function calls: 2243445
Sizzledfy mootools: Time: 1261.888ms, function calls: 50086
Final note
This shouldn't start a Javascript Framework War, I'm not saying mootools sucks. This is just what worked for me and I hope it will work for you too :)
Posted at 6:59 p.m. on August 5, 2010