Extending DOM Nodes with Mootools

September 26, 2006

One of the really useful features of JavaScript is the flexibility of its objects. Developers have spent a lot of time looking for ways to emulate the class system of most Object-Oriented Programming languages, which has been a worthwhile pursuit. Mootools itself gives us one of the most elegant solutions to the problem of defining classes in JavaScript.

But one of the unique aspects of JavaScript is the ability to assign arbitrary properties and methods to any object, regardless of how that object was created. This has led to tricks like extending JavaScript’s native Array and String objects. But did you know you can extend DOM nodes as well?

Attaching methods to Elements

The Mootools library takes full advantage of JavaScript’s ability to attach custom methods to DOM node elements. Unfortunately, because of Internet Explorer’s implementation of DOM nodes, these methods can’t be attached to Element prototypes in a cross-browser fashion. But Mootools does let you pass a reference to a DOM node through the $ function to extend that node with the library’s Element methods. This means that once you’ve called $ on an Element, you don’t have to call it again:

var h1 = $('heading');
h1.setHTML('Updated!');
document.getElementById('heading').appendText('...again.');

The third line of code is purely for demonstration purposes. There’s no need to call document.getElementById on the element, but the point is to show that even if you’re not referencing the variable returned by $, the DOM node itself has been extended.

The Element class in Mootools has been turned into a native object, so you can extend it to add to the methods that get copied onto all DOM nodes passed through the $ function, just like the Mootools system of extending objects. This is similar to extending a native JavaScript object, with the exception that it only works on DOM nodes passed through $ after calling Element.extend:

Element.extend({
  alert: function() {
    alert(this.innerHTML);
  },
  log: function() {
    try {
      console.log(this);
    } catch(e) { alert(this.outerHTML);}
  }
});

$('heading').alert();
$('heading').log();

The $ function also adds an extend method to the DOM node passed to it, allowing individual nodes to be extended with custom methods:

var h1 = $('heading');
h1.extend({
  custom: function() {
    // Custom method for only this element
  }
});

Mootools’ new Element class

The $ function in Mootools extends a DOM node with all the methods of Element.prototype. But Element is also a class that can be instantiated. This is useful for extending a reference you already have to a DOM node that may not have an ID:

var link = new Element(document.getElementsByTagName('a')[0]);

// link contains reference to DOM node that has all methods of Element

Where the Element class really shines is as a shortcut for creating new DOM nodes. If you pass a string into Element’s constructor, Mootools will call document.createElement on that string, then pass the created DOM node through the $ function. This is a very handy shortcut, and cool syntactic sugar:

var h1 = new Element('h1');
h1.appendText('Hello World');
h1.injectInside($E('body'));

Or if you’re comfortable with chaining methods:

var h1 = new Element('h1').appendText('Hello World').injectInside($E('body'));

Subclassing the Element class

This leads to a really expressive capability of Mootools: creating new DOM nodes from custom classes. Remember the old JavaScript image rollover days when the way to preload an image was to call var img1 = new Image();? Now you can use similar custom element constructors in your code to give individual element types streamlined constructors and their own unique methods.

Your first attempt at this would probably be along the lines of trying to extend the Mootools Element class. This doesn’t work, because Element is a native Mootools object. Calling Element.extend adds methods to the Element super class instead of copying the Element class to a new class. Luckily the Element constructor shows us the solution to our problem.

Look at the Element.initialize method and you’ll see that it returns an object. I’m used to thinking in terms of PHP, where class constructors can not return anything because the instantiated object is always assigned from the constructor. In a JavaScript object constructor, any object can be returned. That means we can use our custom Element class’s constructor to create our element, extend the element with the methods of the current class, and then return that element. An example will make this clearer:

var Link = new Class({
  initialize: function(options) {
    options = Object.extend({
      href: '#'
    }, options || {});

    var link = new Element('a');
    link.extend(this);

    for (var i in options) {
      link.setAttribute(i, options[i]);
    }

    return link;
  },

  disableClick: function() {
    this.onclick = function(){ this.blur(); return false; };
    return this;
  },

  enableClick: function() {
    this.onclick = Class.empty;
    return this;
  }
});

The magic happens on the line where we call link.extend(this). When a JavaScript object is instantiated via the new operator, a blank object is created from the prototype of that object’s constructor. That means this inside an object constructor refers to the blank object and contains all the methods attached to the prototype of the constructor. So extending the DOM element we just created with this copies all the methods and properties of our prototype into the DOM node.

We’re also using the Link constructor to set up some simple default parameters to help make creating links easier in the future, and giving links created via this class some simple utility functions for disabling click events. The object could be created with the following:

var link = new Link({href: 'http://www.google.com', title: 'The omnipotent one'});
link.appendText('click here').injectInside($E('body'));
link.disableClick();

This would create a new <a> element that has been extended with Mootools’ DOM node extensions, inserted into the document, and with a disabled click event.

Something else useful — sometimes you’ll have DOM nodes already created that you’d like to give the Link methods to. This can be done in one of two ways. The most direct is to call element.extend, passing in Link.prototype. The other method is to instantiate a new Link object but pass in ‘noinit’ as the parameter, which will keep the initialize method from running when creating the object. Each method performs essentially the same thing and is a matter of personal taste:

var link = new Element(document.getElementsByTagName('a')[0]);
link.extend(new Link('noinit'));

// Accomplishes the same thing:
link.extend(Link.prototype);

Go forth and experiment

Hopefully you’ve seen the expressive power that the Mootools way of handling DOM nodes can give you when writing your own code. Go on and experiment with custom DOM node classes and let me know what you find. One word of warning — be careful when using this.parent() inside a custom DOM element method. I haven’t fully tested it yet, and it seems to work in most cases, but one time it did fail in Internet Explorer while I was trying something on an <iframe> element.

Related resources

Possibly Related:

  • Extending Objects and Classes with mootools
  • Useful Utility Functions in mootools
  • 18 Responses to “Extending DOM Nodes with Mootools”

    1. Yaroslaff "Inviz" Fedin Says:

      Man, thanks for an article, reading it i have the-first-time-impression. And the wittily Link stuff. Just great.

    2. Extendiendo Nodos DOM con Mootools - aNieto2K Says:

      […] Interesante artículo con el cual podremos extender elementos de nuestro DOM mediante el uso de la librería MooTools. […]

    3. mkidder Says:

      Cory, your posts have been quite helpful…. keep up the good work!
      You might want to mention to wrap your DOM related code in some type of loader.

      For example:

      var loader = function(){
      var link = new Link({href: ‘http://www.google.com’, title: ‘The omnipotent one’});
      link.appendText(’click here’).injectInside($E(’body’));
      link.disableClick();
      };

      Window.onDomReady(loader);

    4. Rasmus Says:

      Thanks Cory. This cleared up a lot of things for me! Great tutorial (please make some more!)

      Raz

    5. Tony Trupp Says:

      It seems like the JS framework that’ll end up on top will depend not just on it flexibility, but also largely on the quality of its documentation. Thanks so much on taking the time to do this. MooTools is awsome!

    6. Daily Links at All Things Streaming Vlog Says:

      […] Extending DOM Nodes with Mootools | CoryHudson.com One of the really useful features of JavaScript is the flexibility of its objects. Developers have spent a lot of time looking for ways to emulate the class system of most Object-Oriented Programming languages, which has been a worthwhile pursuit. Mootool (tags: mootools javascript dom tutorials) […]

    7. jairou Says:

      Awsome. I posted this same question in the mootools forum, and Valerio didn’t understand why anyone would want to subclass the Element class. (I’ll admit my post used an overly simplified example). Never the less, I’ve been using a different workaround ever since. Now I can do what I originally wanted using this technique. Thanks for taking the time to document it. I’ll get back to you with some examples after I refactor my code!

    8. Júlio Greff » Arquivo » Estendendo Objetos DOM Says:

      […] Estender objetos DOM vem sendo um problema desde que IE é browser (ou quase isso, IE não é um browser, pode ser usado até como torradeira (?), mas browser não). Como o bichinho azul não nos deixa estender Object ou Element (não me pergunte por quê), nos vemos em um grande problema, que muitos desenvolvedores já tentaramsenvolvedores já tentaram resolver. Um dos mais bem-sucedidos nisso (dos que conheço) foi o Bermonruf, nesse post. Ele usa uma maneira criativa de estender os objetos DOM. Não tão eficiente quanto estender a raíz dos objetos (maldito IE!), mas funciona impressionantemente bem, devido as restrições a nós, pobres mortais, impostas. O post explica melhor a idéia do que eu. Eu, perfeccionista não satisfeito, continuei atrás de uma maneira “elegante”, como diz o Bernardo. Não encontrei, mas encontrei uma maneira diferente, e que se aplica melhor ao meu estilo de desenvolvimento (me ajudou muito, não só com o DOM). Baseia-se em Extending DOM Nodes with Mootools, de Cory Hudson. Como não sou bom pra re-explicar soluções dos outros, leia lá. […]

    9. Marnen Laibow-Koser Says:

      Jairou, I remember that thread. As I recall, the problem wasn’t so much that Valerio didn’t understand why anyone would want to subclass Element; rather, the problem is that JS doesn’t quite have classical inheritance, so that if you do subclass Element, the browser won’t know that it’s still an Element and won’t render it as a DOM object. In other words, while subclassing Element in the conventional sense is possible, it’s pretty much useless in that the new object won’t appear to the browser as an Element. Valerio didn’t explain this very well, but my own experience seems to confirm that this is what he was talking about.

    10. Estendendo Objetos DOM » JulioGreff Says:

      […] Estender objetos DOM vem sendo um problema desde que IE é browser (ou quase isso, IE não é um browser, pode ser usado até como torradeira (?), mas browser não). Como o bichinho azul não nos deixa estender Object ou Element (não me pergunte por quê), nos vemos em um grande problema, que muitos desenvolvedores já tentaram resolver. Um dos mais bem-sucedidos nisso (dos que conheço) foi o Bermonruf, nesse post. Ele usa uma maneira criativa de estender os objetos DOM. Não tão eficiente quanto estender a raíz dos objetos (maldito IE!), mas funciona impressionantemente bem, devido as restrições a nós, pobres mortais, impostas. O post explica melhor a idéia do que eu. Eu, perfeccionista não satisfeito, continuei atrás de uma maneira “elegante”, como diz o Bernardo. Não encontrei, mas encontrei uma maneira diferente, e que se aplica melhor ao meu estilo de desenvolvimento (me ajudou muito, não só com o DOM). Baseia-se em Extending DOM Nodes with Mootools, de Cory Hudson. Como não sou bom pra re-explicar soluções dos outros, leia lá. […]

    11. jeu streap poker Says:

      double bonus poker download…

    12. telecharger evrest poker Says:

      acheter jeu poker…

    13. www produits casino fr Says:

      jeux de casino gratui…

    14. descarga poker Says:

      télécharger gratuitement des jeux de poker…

    15. online poker startguthaben Says:

      gioco di roulette…

    16. come giocare a poker Says:

      scaricare casino gratis…

    17. jouer au casino sans depot Says:

      www geant casino com…

    18. black jack free black jack game black jack odds Says:

      black jack free black jack game…

      By means of virtual online casino flash video poker…