Three-Minute MooTools

By Ryan Florence, published 2010-11-09

Part of the issue MooTools 1.3.

JavaScript <3 objects. MooTools <3 objects. Create them and manage their state with regular looking JavaScript.

// all of MooTools pretty much looks like this.
obj.method();

// or this
var foo = new Something();
foo.method().otherMethod();

Native Types

MooTools Extends the Native Types with new methods

// JavaScript array API
[2,1,3].sort(); // => [1,2,3];

// MooTools array API
[2,1,3].getLast(); // => 3

Creates generics out of the Type methods

// JavaScript method "borrowing"
Array.prototype.slice.call(arguments, 1);

// MooTools
Array.slice(arguments, 1)

Adds Type functions where it makes sense

// ECMAScript 5 object function
Object.keys({one: 1, two: 2}); // => ['one', 'two']

// MooTools
Object.values({one: 1, two: 2}); // => [1,2]
Array.from(arguments)

Events

Browser’s event APIs vary. MooTools fixes it.

// JavaScript event
document.body.addEventListener('click', function(event){
	console.log(event)
}, false);

// MooTools cross-browser event
document.body.addEvent('click', function(event){
	console.log(event);
});

Custom events share the same API

var fx = new Fx.Morph('some-element');
fx.addEvent('complete', function(){
	// do something
});

Constructors (Classes)

JavaScript has constructors, MooTools has constructors, called Classes. Construct them and then manage their state.

// JavaScript constructor
var date = new Date();
date.getTime();

// MooTools constructor
var req = new Request.JSON({
  url: '/gimme/some/JSON',
  onComplete: function(json){ console.log(json) }
});
req.send();

Most classes take an options argument:

var fx = new Fx.Morph('some-element', {
	duration: 2000,
	transition: 'back:out'
});

Prescribed Code Organization

Keep the logic out of your application code and then write some tests!

Classes are constructors with code reuse built in

Classes share code via Extends and Implements.

var Toggler = new Class({

    Extends: Fx.Tween, // sets Fx.Tween as the prototype of Toggler
    Implements: Loop, // copies methods from Loop

    options: {
      states: [1, 0],
      property: 'opacity'
    },

    initialize: function(element, options) {
      this.parent(element, options); // call super method
      this.setLoop(this.toggle, this.options.duration); // copied (not inherited) from Loop
      this.toggled = false;
    },

    toggle: function(how){
      if (how == 'in') {
          this.toggled = true;
          return this.start(this.options.states[1]); // inherited method `start`
      }
    
      if (how == 'out') {
          this.toggled = false;
          return this.start(this.options.states[0]);
      }
    
      return this.toggle(this.toggled ? 'out' : 'in');
    }

});

// use it like every other JavaScript object created with a constructor
// and every other class in MooTools
var fx = Toggler('some-el', {
	property: 'height',
	states: [400, 0]
});

// call methods on it when something happens
fx.toggle(); // down to 0
fx.toggle(); // up to 100
fx.startLoop(); // starts toggling over and over and over (Loop is my own plugin)

Element Getters and Setters (Properties)

// Normal element properties
$('el').get('title');

// defining a new property
Element.Properties.toggle = {

  set: function(options){
    this.get('toggler').setOptions(options, true);
    return this;
  },

  get: function(){
    var instance = this.retrieve('toggler');
    if (!instance){
      instance = new Toggler(this);
      this.store('toggler', instance);
    }
    return instance;
  }

};

// Now you can get and set toggler
$('el').set('toggle', { property: 'width', states: [200, 500]});
$('el').get('toggle');

Type Extensions put your code in natural places, maintaining JavaScript’s native API

View this on jsFiddle

Array.implement({ 

    intersection: function(array) {
      var clone = Array.clone(this);
      for (var i = 0, l = clone.length; i < l; i++){
        if (!array.contains(clone[i])) clone.erase(clone[i])
      }
      return clone;
    }

});

var a = [1,2,3,4,5,6],
    b = [0,2,4,6];
    c = a.instersection(b); // => [2,4,6]

Putting it all together

For an Element-based plugin:

  1. Create a class to store the logic
  2. Add an Element property to manage an instance of the class
  3. Extend Element with new methods that manage the state of the instance

Only thing left to do is #3.

View this on jsFiddle

Element.implement({
  toggle: function(how){
    this.get('toggle').toggle(how);
    return this;
  }
});

// usage
$('some-el').toggle()

Continue on to the next article in this issue: MooTools Types 1.3.

Hi, I'm Ryan!

Location:
South Jordan, UT
Github:
rpflorence
Twitter:
ryanflorence
Freenode:
rpflo

About Me

I'm a front-end web developer from Salt Lake City, Utah and have been creating websites since the early 90's. I like making awesome user experiences and leaving behind maintainable code. I'm active in the JavaScript community writing plugins, contributing to popular JavaScript libraries, speaking at conferences & meet-ups, and writing about it on the web. I work as the JavaScript guy at Instructure.