Object Oriented jQuery With MooTools (Pigs Take Flight)

By Ryan Florence, published 2010-03-04

Part of the issue Migrated Articles From Original Site Structure..

Update: This has become quite popular so I’ve created a site dedicated to the technique at moo4q.com. Please don’t use the code from this article as the new code at moo4q.com is better.

Check this out:

The short story

Here’s the recipe: MooTools modules Native, Class, and Class.Extras (24k uncompressed); jQuery; and Class.Mutatotors.jQuery. Write a MooTools Class and use jQuery for the DOM.

Class.Mutatotors.jQuery

This mutator automatically extends the jQuery object with the class, so you can use conventional jQuery syntax to instantiate and control the class.

Class.Mutators.jQuery = function(name){
        var self = this;
        jQuery.fn[name] = function(arg){
                var args = Array.prototype.slice.call(arguments, 1);

                if ($type(arg) == 'string'){
                        var instance = jQuery(this).data(name);
                        if (instance) instance[arg].apply(instance, args);
                } else {
                        jQuery(this).data(name, new self(this.selector, jQuery.extend(self.prototype.options, arg)));
                }
        };
};

Use it in a class like so:

var MyClass = new Class({
  Implements: [Options,Events], 
    options: { },
  
  jQuery: 'whateverYouWant'
  
  initialize: function(selector){
    this.elements = $(selector);
  },
  
  method: function(arg, arg2){
    // do stuff
  }
  
});

Now you can use MyClass in two ways, the conventional MooTools way, or the conventional jQuery way:

// instantiate
$('#el').whateverYouWant(); // jQuery convention
var object = new MyClass('#el'); // or MooTools convention

// call methods
$('#el').whateverYouWant('method', arg, arg2); // jQuery
object.method(arg, arg2); // MooTools

Download it from moo4q.com

The whole story

One day while goofing with some jQuery it hit me that it’s totally different than what I tend to think of as a JavaScript framework. As pointed out in jqueryvmootools.com, “If jQuery makes the DOM your playground, MooTools aims to make JavaScript your playground”. As Thomas Aylott puts it, jQuery is a Domain Specific Language. It is the most popular DOM ninja.

Many articles (like mine) compare the similar features of MooTools and jQuery, but few talk about what makes them unique. I kind of think of it as Mac v. Windows. It’s a silly comparison, really. Mac is hardware + os, windows is just an os. The scopes of MooTools and jQuery are different.

So what’s different?

jQuery is a single toolkit with many DOM-focused features while YUI, Dojo Toolkit, MooTools and many others are more modular. MooTools is completely modular and can be custom-built with individual modules. One of MooTools’ core modules is called Element. It, along with the other core modules which depend on MooTools’ Element, can combine to be practically the equivalent to the single jQuery toolkit as a whole.

The syntax for MooTools’ Element module is almost 100% search and replaceable to convert to jQuery and vice-versa:

var styles = {
  color: '#333',
  height: 500 
};

var animation = {
  left: 100,
  top: 200
};

// MooTools
$('elementId')
  .setStyles(styles)
  .morph(animation);

// jQuery
$('#elementId')
  .css(styles)
  .animate(animation);

So, if Element (with it’s dependencies) and jQuery as a whole are practically the same, what’s the big difference? Remove the overlap of the two frameworks and you’re left with the inheritance model of MooTools found in it’s module Class and the native extensions found in Native. Most everything else in MooTools (Element, Request, Fx) can generally be found in jQuery in one form or another, though the usage and features may vary.

Since MooTools encourages you to pull in only the modules you need, what happens if you use only Native and Class from MooTools to get an inheritence model and plug in jQuery for everything else? You get something pretty cool, and a potential springboard for the future of javascript frameworks. Yep, jQuery as a plugin for MooTools. Or inversely and maybe more appropriate, MooTools object oriented programming for jQuery. The MooTools file should only weight about 24k uncompressed.

In my mind, they aren’t competing frameworks.

You can use a mootools utility class inside a jQuery plugin, or you can write a class with MooTools and use jQuery to handle all the DOM manipulations and effects, if that’s how you want to roll. So how would you do it and what does it look like?

I remember a couple weeks ago Thomas Aylott and I were talking about the concepts that led to this post. He went ahead and submitted jQuery as a MooTools plugin to the forge, and a stripped down build of MooTools as a plugin for jQuery, both were removed! Some even went so far as to question Thomas’s sanity.

Using a MooTools class inside of a jQuery plugin

I’ve got a utility class I use all over the place: Loop.js. It has nothing to do with the DOM so it’s the perfect starting point for this experiment. Loop is really just a periodical, or interval manager. I can start the interval, stop it, ask if it’s currently looping or if it’s stopped, etc. Ideally it’s implemented into another class, but it can be used on it’s own too like so:

var fn = function(){ /* do stuff */ };
var loop = new Loop();
loop.setLoop(fn, 1000).startLoop(); // calls fn every second

// later
loop.stopLoop();

If you’re not familiar with MooTools, I instantiated a class on line 2. On lines 3 and 6 I called a couple methods. That’s a very common thing you’ll see with MooTools. Instantiating objects, and then calling methods on them. To illustrate how to use a MooTools utility class in a jQuery plugin, here’s animateSprite. It cycles the background-position of an element to make animations out of sprites like this one, check out how Loop is used here as a utility.

jQuery.fn.animateSprite = function(options){

        var settings = jQuery.extend({
                frameWidth: 75,
                frames: 10,
                frameRate: 100,
                defaultPosition: {x: 0, y :0}        
        }, options);

        this.each(function(){

                var element = jQuery(this);
                element.css({'background-position': settings.defaultPosition.x + 'px ' + settings.defaultPosition.y + 'px'});

                var computeX = function(){
                        looper.loopCount = (looper.loopCount == (settings.frames)) ? settings.defaultPosition.x : looper.loopCount;
                        return -looper.loopCount * settings.frameWidth;
                };

                var step = function(){
                        var x = computeX();
                        var y = settings.defaultPosition.y;
                        element.css({'background-position': x+'px '+ y+'px'});
                };
                
                // mootools class Loop handles periodical (interval) gracefully
                var looper = new Loop().setLoop(step, settings.frameRate).startLoop();

        });
};

Every time I make a jQuery plugin that needs to run a function on an interval I’d probably have to goof around with setInterval, which isn’t all that hard, it’s just ugly! Instead, I can just use Loop. In computeX I ask my looper what the current value is for it’s property loopCount and then figure out what to return as the next x position for the background. Then I create my Loop instance and set step as the function to be looped. Tell me that’s not prettier than setInterval.

You use this plugin just like any other:

$('#spinner').animateSprite({
  frameWidth: 64,
  frames: 10,
  frameRate: 100
});

So you can easily use a MooTools Class inside of a jQuery plugin. However, I can’t start and stop this spinner with the current plugin. I’m sure I could do some things to the plugin but it’s not common with jQuery plugins to be able to manipulate them later. By that I mean, they usually require a specific html structure requiring two buttons to start and stop this thing upon which the plugin would bind some click events. Note I didn’t say it wasn’t possible to start and stop some other way, I’m simply saying that once you create the thing, it is what it is 99% of the time.

But what about the the other way around … what if I wanted to keep my plugin logic in the MooTools Class format? (Which I do!)

What a MooTools Class looks like

If you’re coming from jQuery a MooTools class can be thought of as a jQuery plugin.

var SomeClass = new Class({
  
  Extends: SomeParentClass, // inherits everything from this guy
  Implements: [Options,Events], // copies methods from these guys
  
    options: {
      // just like jQuery plugin defaults
    },
    
  initialize: function(element, options){
    // gets called when the object is constructed
    // because of it's magical name `initialize`
    this.setOptions(options); // sets options to defaults or user defined
                              // copied from Options up in Implements                          
  },
  
  doSomething: function(arg){
    this.parent(arg); // calls SomeParentClass's doSomething method
    // does stuff in addition to SomeParentClass
    this.fireEvent('doinStuff'); // custom event or 'callback' in jQuery
  }
});

And here’s how you use it, usually after a DOM ready event.

// construct it
var myInstance = new SomeClass('elementId', options);

// and add events to it
myInstance.addEvent('onDoinStuff', function(){
  alert('holy crap! mootools and jquery in the same script!')
});

// and can call methods on it
myInstance.doSomething();

This kind of code is not only powerful but also easy to debug and maintain. Hopefully that makes enough sense for the rest of this article.

A MooTools Class with jQuery instead of MooTools’ Element module

I don’t think there’s much of an argument here: a well structured class in MooTools is prettier than a well structured plugin for jQuery. They can’t all be California girls. But more importantly, you can extend a MooTools class and reuse code whereas with a jQuery plugin you can’t reuse the code as easily.

Check out this version of animateSprite with it’s new name of SpriteAnimation:

var SpriteAnimation = new Class({

        Implements: [Options, Loop],
 
                options: {
                                frameWidth: 75,
                                frames: 10,
                                frameRate: 100,
                                defaultPosition: {x: 0, y :0}
                        },

        initialize: function(element, options){
                this.setOptions(options);
                this.setLoop(this.step, this.options.frameRate);
                this.element = jQuery(element)[0]; // jQuery!
                var dp = this.options.defaultPosition;
                this.element.css({'background-position': dp.x + 'px ' + dp.y + 'px'}); // moar jQuery!
                this.startLoop();
        },

        step: function(){
                var x = this.computeX();
                var y = this.computeY();
                this.element.css({'background-position', x+'px '+ y+'px'}); // it's taking over!
                return this;
        },
    
        computeX: function(){
                this.loopCount = (this.loopCount == (this.options.frames)) ? this.options.defaultPosition.x : this.loopCount
                return -this.loopCount * this.options.frameWidth;
        },
    
        computeY: function(){
                return this.options.defaultPosition.y;
        }

});

Did you see the jQuery in there? Out of 36 lines only 3 of them had any jQuery at all (8.3%). So how do you use it?

var spinner = new SpriteAnimation('#spinner',{
  frameWidth: 64,
  frames: 10,
  frameRate: 100
});
 
// call methods
spinner.stopLoop();
spinner.startLoop();

Yeah, for real. Nutty isn’t it? Here’s the REALLY nutty part.

What jQuery user is going to mess with all that new business? Not you! You love jQuery because it’s “designed to change the way you write javascript.” So let’s do it. With the help of Christoph Pojer and Olmo Maldonado I’ve created a script called Class.Mutators.jQuery.js. You may want to cover their eyes, this code is graphic…

Class.Mutators.jQuery = function(name){
        var self = this;
        jQuery.fn[name] = function(arg){
                var args = Array.prototype.slice.call(arguments, 1);

                if ($type(arg) == 'string'){
                        var instance = jQuery(this).data(name);
                        if (instance) instance[arg].apply(instance, args);
                } else {
                        jQuery(this).data(name, new self(this.selector, jQuery.extend(self.prototype.options, arg)));
                }
        };
};

A Mutator is something that happens when a class is created, not when it is instantiated. Extends and Implements are mutators. So in my SpriteAnimation script I simply add this to the class definition:

var SpriteAnimation = new Class({
  
  jQuery: 'animateSprite'  // pass in what you want the jQuery method name to be

  //...

And like magic I can now do things the conventional jQuery way:

$('#spinner').animateSprite({
  frameWidth: 64,
  frames: 10,
  frameRate: 100
});

But more even more awesome is that I can call methods on it too:

$('#ajax-button').ajaxComplete(function() {
  $('#spinner').animateSprite('stopLoop');
});

// or if the method supports arguments, just toss them in!
$('#spinner').animateSprite('someMethod','arg1', 'arg2', 'arg3');

All that you get from Class.Mutators.jQuery.

Why you may want to write your jQuery plugins with MooTools

Inheritance.

What if I wanted to cycle the y position rather than the x position? With jQuery I’d need a whole new plugin or I would go hacking around inside the current one. But if you’re writing your plugin with MooTools Class you can simply extend SpriteAnimation:

SpriteAnimationY = new Class({
  
  Extends: SpriteAnimation, // inherits everything
  jQuery: 'SpriteAnimationY', // extends jQuery object
  
    options: {
      frameHeight: 100 // new option, others are still there
    },
  
  computeX: function(){
    return this.options.defaultPosition.x; // overwrites old method
  },
  
  computeY: function(){
    this.loopCount = (this.loopCount == (this.options.frames)) ? this.options.defaultPosition.y : this.loopCount;
    return -this.loopCount * this.options.frameHeight; // uses new option
  }
  
});

Seriously, that was easy. Now we can have a vertical sprite without hacking around in the original plugin or needing to rewrite the rest of the logic contained in SpriteAnimation. When you extend a Class you inherit everything from the parent class, any method with the same name in your subclass will overwrite the parent’s. Often, you still need the parent method, so you can call with a simple this.parent().

So far we’ve looked at three Classes: Loop, SpriteAnimation, and SpriteAnimationY. SpriteAnimationY inherits everything from SpriteAnimation, and SpriteAnimation implements all the methods of Loop. For small projects this isn’t incredibly important but still helpful, but with larger web applications having modular javascript like this is not only dang cool, but critical to keeping your code base sane.

The real challenge, Slideshow!

SpriteAnimation only had three jQuery methods, but my script Slideshow has a ton of DOM manipulations and animations–a perfect candidate for testing this Object Oriented jQuery silliness. I spent about 30 minutes porting it to this hybrid format. For more information about SlideShow, check out the MooTools version.

What do you think?

So, what do you think? Just a trick? Paradigm shifting? Will you use it? If nothing more I hope this gets some people thinking differently about javascript frameworks and modularity. I can imagine a world where you choose Slick or Sizzle for a selector engine, MooTools or some other framework for writing classes and application logic, and then a final DOM kit like jQuery or MooTools’ Element to actually work with the elements.

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.