Saturday, 19 February 2011

Nested Elements in MooTools

When developing a web application my usual javascript library of choice is MooTools since it is simple to use, and provides cross-browser support along with a form of object-orientation.

The library has an Element structure that allows for the easy construction of HTML elements, but the way those elements are tied together is based on functions such as inject(), adopt() and grab(). This works well, but I was recently constructing a fake dialog box consisting of many nested elements and wanted to be able to declare them in a way that clearly shows the hierarchy.

This would be one way of doing it with MooTools:
var dialog       = new Element('div.background');
var container    = new Element('div.container').inject(dialog);
var window       = new Element('div.window').inject(container);
var header       = new Element('h1', { text: 'A title' }).inject(window);
var content      = new Element('div.content').inject(window);
var bar          = new Element('div.bar').inject(window);
var okButton     = new Element('button', { text: 'OK', events: { ... }}).inject(bar);
var cancelButton = new Element('button', { text: 'Cancel', events: { ... }}).inject(bar);

However, although it is fairly tidy and concise, it is difficult to just look at the code and determine the resulting structure. Instead I wanted to be able to do this:
new Element('div.background', { children: [
    new Element('div.container', { children: [
        new Element('div.window', { children: [
            new Element('h1', { text: 'A title' }),
            new Element('div.content'),
            new Element('div.bar', { children: [
                new Element('button', { text: 'OK', events: { ... }}),
                new Element('button', { text: 'Cancel', events: { ... }})
            ]})
        ]})
    ]})
]});
using a property that would allow me to declare child elements.

One of the most powerful features of MooTools is that it provides methods to extend the toolkit further. I have already taken advantage of this in the past by adding a search function to the Array class that takes a predicate function. The Element structure, however, is not an actual MooTools class and therefore cannot be easily extended. I did find an example that created a new utility function which allows child elements to be specified, but I wanted to simply enhance the existing Element.

The best way of enhancing Element is to add to the global element property set as follows, then the corresponding get/set in the Element structure simply use that property when they find it:
Element.Properties.children = 
{
    get: function()      { return this.getChildren(); },
    set: function(value) { this.adopt(value);         } 
};