5 Advanced Techniques for MooTools Development

Tuesday, January 13th, 2009 @ 11:30 pm | filed under: Best Practices, MooTools

The documentation for MooTools is robust and covers nearly everything in the library. There are a few items left out that are either not there on purpose (because they are not guaranteed to be supported in future releases) as well as several standard JavaScript techniques that can empower your development. Here are 5 things that you should know that aren’t obvious.

1. Class.toElement and $

The $ function is used to retrieve elements from the DOM by their id and also used to ensure that an element reference has been extended by MooTools, but did you know you can write your classes so that $ returns an element when passed an instance of your class? All you have to do is have a method called toElement that returns the element you want.

[js]var Widget = new Class({
initialize: function(element) {
this.element = $(element); //store the element reference
},
toElement: function() {
return this.element;
}
});
//create an instance
var myWidget = new Widget($(‘foo’));
//get the element again
$(myWidget) == $(‘foo’);[/js]

This works with, for example, Fx.Tween or Fx.Morph. I use in all my classes that reference a single element (such as Waiter or StickyWin). I even have a mix-in class called ToElement that you can implement into your classes that provides this for you:

[js]var Widget = new Class({
Implements: ToElement,
initialize: function(element) {
this.element = $(element); //store the element reference
}
});
//create an instance
var myWidget = new Widget($(‘foo’));
//get the element again
$(myWidget) == $(‘foo’);[/js]

2. Use Element Storage to Go the Other Direction

In the section above I show you how to go from an instance of a class to the element it enhances, but what if you want to go the other way? That’s where Element Storage comes in handy. When you create your instance store the reference to that instance on the element and then you can retrieve it later:

[js]var Widget = new Class({
initialize: function(element) {
this.element = $(element); //store the element reference
this.element.store(‘Widget’, this); //store this class in the element
}
});
var myWidget = new Widget($(‘foo’));
//retrieve the class from the element
$(‘foo’).retrieve(‘Widget’);[/js]

Just like my ToElement mix-in, I also have a mixin that does this for me called Occlude. What’s more, it allows me to prevent a class being applied to the same element twice. Imagine if you had two instances of Fx.Tween running on the same element – they’d be fighting each other if you ran them both at the same time. By checking the element to see if there is already a class applied to it, I can ensure there’s no collision.

[js]var Widget = new Class({
implement: Occlude,
property: ‘Widget’, //the name used to store this instance on the element
initialize: function(element) {
this.element = $(element); //store the element reference
//if the element already has an instance of this stored as ‘Widget’
if (this.occlude()) {
//return that instance and exit
return this.occludes;
}
}
});
var myWidget1 = new Widget($(‘foo’));
var myWidget2 = new Widget($(‘foo’));
myWidget1 == myWidget2;[/js]

3. Use Closures to Extend Native Methods

MooTools lets you easily add methods to native objects like Array, String, and Function, but what if you want to augment the MooTools method that’s already there? Unlike Classes, there’s no extend method that let’s you reference a “parent” version of a method you overwrite. That’s where closures come in handy.

Simply make a reference to the previous version of the method, then implement a new one referencing the old one.

[js](function(){ //encapsulate this logic so we don’t pollute the global namespace
var oldSetStyle = Element.prototype.setStyle;
Element.implement({
setStyle: function(property, value) {
//lets add a new custom event to Element that fires
//show/hide when the display style changes
if (property == ‘display’) {
if (value == ‘none’) {
this.fireEvent(‘hide’);
} else {
if (this.getStyle(‘display’) == ‘none’) {
this.fireEvent(‘show’, value);
}
}
}
//continue on to the previous version of this method
oldSetStyle.apply(this, arguments);
}
});
})();
window.addEvent(‘domready’, function(){
$(‘foo’).addEvents({
show: function(display) {
if (display == ‘block’) alert(‘displaying element!’);
},
hide: function() {
alert(‘hiding element!’);
}
});
});
[/js]

4. Measure the Dimensions of a Hidden Element

MooTools has numerous methods for measuring the location and size of an element, but they don’t work when the element is hidden. How do you figure out the height of something when it’s hidden without showing it and producing a ‘flicker’ as it pops in and out. The trick is to show it (display:block) but also hide it (visibility:hidden) and to set it’s position to absolute so that it doesn’t affect the page flow. Then you put it all back where it was after you finish measuring it.

Here’s a simple extension that does that for you:

[js]Element.implement({

expose: function(){
if (this.getStyle(‘display’) != ‘none’) return $empty;
var before = {};
var styles = { visibility: ‘hidden’, display: ‘block’, position:’absolute’ };
//use this method instead of getStyles
$each(styles, function(value, style){
before[style] = this.style[style]||”;
}, this);
//this.getStyles(‘visibility’, ‘display’, ‘position’);
this.setStyles(styles);
return (function(){ this.setStyles(before); }).bind(this);
}

});
var putItBack = $(‘foo’).expose();
var size = $(‘foo’).getSize();
putItBack();[/js]

This method is included in my Element.Measure plugin.

5. Use Element.Properties to Create “Built-in” Instances of Classes

MooTools has a collection of properties defined for elements to reference called Element.Properties. This object is just an object containing references to special values used by Element.get/set. By defining your own get/set properties you can add a custom getter and setter for your class. Then, with Element.implement you can create methods on element that let you reference these “Built-in” instances.

Consider Fx.Tween and Element.tween. When you fetch an element out of the DOM, it doesn’t have an instance of Fx.Tween yet, but the second you call myElement.tween(…) an instance is created. Subsequent calls to myElement.tween continue to reference that same instance.

This combines the second tip above by storing the instance on the element. I use this in many of my classes including Fupdate and Fx.Reveal. Here’s what it looks like in Fx.Reveal:

[js]Element.Properties.reveal = {

set: function(options){
//get the instance of reveal already created for this element if there is one
var reveal = this.retrieve(‘reveal’);
if (reveal) reveal.cancel(); //if found, cancel it
//eliminate the instance from storage, and re-store the options
return this.eliminate(‘reveal’).store(‘reveal:options’, $extend({link: ‘cancel’}, options));
},

get: function(options){
//if options are passed or reveal isn’t stored on this element yet
if (options || !this.retrieve(‘reveal’)){
//store the options
if (options || !this.retrieve(‘reveal:options’)) this.set(‘reveal’, options);
//store a new instance of Fx.Reveal
this.store(‘reveal’, new Fx.Reveal(this, this.retrieve(‘reveal:options’)));
}
//return the instance
return this.retrieve(‘reveal’);
}
};

//implement element methods
Element.implement({
//element.reveal(options) will store the options (if passed in) and
//then call the .reveal method. By using .get(‘reveal’) an instance is
//created if one doesn’t yet exist.
reveal: function(options){
this.get(‘reveal’, options).reveal();
return this;
},

//dissolve works the same way, only calling the .dissolve method
dissolve: function(options){
this.get(‘reveal’, options).dissolve();
return this;
}
});[/js]

By defining custom setters/getters you empower your classes to have information about elements that they are attached to by default and ensure that your element methods refer to the same instances over and over again except when you intend to discard them.

8 Responses to “5 Advanced Techniques for MooTools Development”

  1. kenton Says:

    Wow this is incredible, I thought I knew mootools in and out but every single one of these was AMAZING and something i always wanted to know how to do!

  2. Thomas Allmer Says:

    this is really amazing. That’s one nice way of using MooTools. The more I learn about MooTools (and it’s users) the more I’m impressed.
    Keep up the good work and keep those nice code snippets coming. (reading your code is like reading a book… :) )

    PS: a small typo in 5: you can add a custom !!gettter!! and setter for your class

  3. Aaron N. Says:

    @Thomas, Thanks. Fixed.

  4. Ajaxian » MooTools Update: Milk, MooBugger, JavaScript techniques, and more Says:

    [...] 5 Advanced Techniques for MooTools Development [...]

  5. steida Says:

    ad 4. – http://pastie.org/363273

  6. Aaron N. Says:

    This is excellent work Daniel! I’m going to incorporate this into my library at once!

  7. Tim Lund Says:

    Nice tips Aaron. I can remember I needed #4 a couple of months ago. Can’t remember how I got around it without your tip though :)

  8. Timothy Says:

    Thanks a lot for the info. Much appreciated