Useful Utility Functions in mootools
September 14, 2006
The new mootools JavaScript framework has quickly impressed me with its design and usefulness. The library was clearly written to meet real programmers’ needs while working in JavaScript. Just take a look at some of the new utility functions and methods it provides.
Note: This article covers functions and methods found in the Array.js and Function.js modules of mootools.
A cleaner array iterator
Mootools provides a Prototype-inspired each method that every JavaScript array inherits. Its syntax is a bit cleaner because it’s based on the Mozilla Array.prototype.forEach method. The mootools each method allows you to pass in a second parameter as the context in which the function will be called, allowing you to resolve the this keyword. Accomplishing this in the Prototype library involves binding the function passed to each:
var x = {sum: 0};
for (var i = 0, fixtures = []; i < 500; i++) {
fixtures.push(i);
}
fixtures.each(function(el, i) {
this.sum+= i
}.bind(x));
// x.sum = 124750
Accomplishing the same thing in mootools is just a small syntax change (the changed line is highlighted):
var x = {sum: 0};
for (var i = 0, fixtures = []; i < 500; i++) {
fixtures.push(i);
}
fixtures.each(function(el, i) {
this.sum+= i
}, x);
// x.sum = 124750
The mootools each method takes the object that will resolve to this inside the loop as the second argument.
Why should you care about this minor syntax change? Because calling bind on a function every time through a loop is going to add overhead to your code’s performance. I’ve run the previous code in Firebug with a timer and the results are very telling:
500 elements each with bind: 39ms
500 elements each: 3ms
1000 elements each with bind: 75ms
1000 elements each: 6ms
Not only is the mootools syntax cleaner, but it performs almost 10 times faster than using the bind method. Keep in mind that these examples all use mootools’ each method. If I’d used the Prototype each the code would have run even slower, but that issue has already been well-documented.
Prototype each diversion
The reason Prototype’s each loop runs so slowly is that each iteration of the loop has to run through a try/catch block in order to look for break and continue statements. If you’re using Prototype’s each and you want to break or continue in the loop, you must use the following syntax:
[1,2,3,4,5,6,7,8,9,10].each(function(el) {
if (el > 7) throw $break;
if (el == 4) throw $continue;
alert(el);
});
// Will alert "1", "2", "3", "5", "6", "7"
I’m not here to bash Prototype. It’s a great library. But I think it does need to be pointed out that its each method will run slower than traditional loops because of the design decision to incorporate a workaround for break and continue statements.
What, you’re not using break and continue statements? They can really help you cut down on loop iterations. There is no way to use break or continue in an each loop in mootools, so keep that in mind. You’ll have to use the good old-fashioned for loop for that one.
Running code at time intervals
JavaScript provides the setTimeout and setInterval functions for running code after a delay or at regular intervals. This comes in very handy for animations, but also for other GUI functions, such as delaying hiding a menu after navigating away. The native syntax for this can get a little verbose sometimes, so mootools provides some utility functions as methods of every function. To run code once after a time delay you use the delay method:
(function(){ alert("Sorry I'm late."); }).delay(5000);
This will run the function after a delay of 5 seconds. You can also run code at a regular interval using the periodical method:
var annoy = (function(){ console.log('Are we there yet?'); }).periodical(5000);
The previous code will run until the window unloads unless we clear the interval returned by periodical. Mootools provides a $clear function that will clear any time interval passed to it, whether it was created with delay or periodical:
$clear(annoy);
Just be sure to assign the periodical function to a variable, or else you won’t be able to clear it! Talk about annoying…
The second parameter to delay and periodical is the context object to call the function within, just like the each method. This can come in handy when using methods of mootools’ objects:
var xhr = new Ajax('url', {update: ajaxElement});
var $req = xhr.request.periodical(60000, xhr);
This code will create an Ajax object that calls its request object every minute. You need to pass the Ajax object as the second argument to periodical so the request method will run in the right context.
Cleaner object detection
Sometimes checking the type of a JavaScript object can get confusing. Everything is actually an Object, so running [1,2,3] instanceof Object returns true, even though we were checking against an array. But curisouly, running 'string' instanceof Object returns false! There’s also the typeof function, so things can get rather confusing and frustrating in a hurry when trying to detect object types. Mootools provides a function called $type that will do the proper checks behind the scenes and return a lowercase string type of the element you passed into the function. If the type of object is not matched, $type will return false.
The types returned by $type are:
- function
- textnode
- element
- array
- object
- string
- number
- false
Try it out:
$type(function(){});
// Returns 'function'
$type(document.createElement('div'));
// Returns 'element'
$type([]);
// Returns 'array'
$type({});
// Returns 'object'
$type('hello world');
// Returns 'string'
$type(4);
// Returns 'number'
Mootools just continues to amaze me. I’m digging through the source code and running lots of tests to see just how the library works, so stay tuned for more articles about this new library’s features.
September 14th, 2006 at 4:59 am
Nice post. Very useful. I’ve also posted on how to create a simple lightBox example using the new mooTools.
September 14th, 2006 at 6:51 am
Cory, there’s also a define function, that allows to use delay & function with arguments without lambda
function myfunc(f) {
alert(f);
}
myfunc.define(’sup’).delay(600);
not sure in syntax right now.
September 14th, 2006 at 1:40 pm
@Yaroslaff: The define function looks handy. It looks like it’s for binding arguments to a function, like currying and partial application. Is this correct? I’ve looked through the latest mootools release and can’t find the define method anywhere. Is this a feature coming soon? It would really come in handy.
September 14th, 2006 at 4:53 pm
Yeah, Cory, It looks like Valerio removed define method from function.js. He prolly had a good reason for this. But without eval i’d do it like this:
define: function(a,b,c,d,e,f,g,h,i,j) {
return (function(){this(a,b,d,c,d,e,f,g,h,i,j);});
}
which just does dirty job for you.
September 14th, 2006 at 5:15 pm
[quote]
What, you’re not using break and continue statements? They can really help you cut down on loop iterations. There is no way to use break or continue in an each loop in mootools, so keep that in mind. You’ll have to use the good old-fashioned for loop for that one.
[/quote]
You don’t realy need to use the for loop in that case…
If You wan’t to “emulate” the continue statement just use “return” inside your function (since .each uses a function and not a block this feels more natural for me and using “throw $break” feels kinda stupid)
If you need a break statement you can easily emulate it using try/catch like prototype does (writing an extra function to abstract the try/catch block away will be very easy)
e.g.
[1, 2, 3].each(
function(x){
if(x == 2) return;
else alert(x);})
September 14th, 2006 at 5:26 pm
you cant emulate break, but continue. return will act only as continue.
You can set a check, but it wont be the same.
September 15th, 2006 at 10:51 am
try this:
throw $break;
[code]
var $break = new Object();
try{
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].each(
function(x){
if(x
alert(x);})
}catch(e){
if(e != $break) throw e;
}
[/code]
This alert all numbers from 5 to 8, but won’t run the function for the value 10, cause using “throw $break” we’ve finished looping earlier…
This is, what Prototype uses whenever you call .each…
we may make it more readable:
[code]
var $break = new Object();
function break(){ throw $break; }
Array.prototype.myEach = function(fn, bind){
try{ this.each(fn, bind); }catch(e){ if(e != $break) throw e; }}
//and finally write:
break();
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].myEach(
function(x){
if(x
alert(x);
});
[/code]
Now you may use .myEach, if you need looping with break and each if you don’t need to break out of a loop…
September 20th, 2006 at 9:15 am
@Yaroslaff “Inviz” Fedin: The function is now there, and its called pass. Its a shortcut to create anonymous functions that contains calls to actual functions with parameters. example:
before:
$('myelement').onclick = function(){
$('myElement').addClassName('myClass');
};
after:
$('myElement').onclick = $('myElement').addClassName.pass('myClass');
October 4th, 2006 at 11:26 am
Cory, please keep up with the articles, I like the look of mootools as a route into learning OO JS rather than trad functional programming, but documentation is a bit thin so far so your pieces are very welcome.
You obviously know what you are talking about so maybe you could explain something very simple to me (it’s the abstract of OO vs the specifics of functional programming that is confusing me).
If, as in one of Valerio’s tutorials which I’ve copied some of below, a div is created which has its properties defined in a stylesheet, I can
fx.Background = Class.create();
fx.Background.prototype = Object.extend(new fx.Base(), {
initialize: function(el, options) {
this.el = $(el);
alert( … );
this.setOptions(options);
this.now = 0;
}, …
);
window.onload = function() {
myNewEffect = new fx.Background(’examplescrolling’, {duration: 500});
}
I added the alert box there. I can get some properties there eg this.el.offsetHeight will return the correct value, but none of the style properties are available, eg this.el.style.top doesn’t return anything. I can set style properties there but not read them (ie from the stylesheet declarations). It is such a fundamental problem I’m assuming the answer is really simple..
October 7th, 2006 at 12:52 pm
wow, [runs off to change pants]
[looks at mate] did you see those response times? freakin awesome!!!!
July 16th, 2007 at 7:35 am
In prototype in the PeriodicalUpdater function, there is an option called decay that lets the call frequency “decay” if the result does not change from one call to the next. Is anyone aware whether this functionality exists in mootools Periodical?
I just discovered mootools and js frameworks on the whole, and i’m keen to use mootools due to everything mentioned above…