sans-serif   serif

«

Object Oriented jQuery with MooTools (Pigs take flight)

March 4, 2010

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 site 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 mootools-jquery.js

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.

Related Posts:

  • No Related Posts

Comments

  • http://roarofthefagiano.blogspot.com/ Andrea

    Woah!

    This is a great script. Well done. =)

  • http://www.kromack.com/ Kromack

    This is amazing !

  • http://www.sook.com.br Danillo César

    Great man, now jQuery users can use Mootools Class style in yours plugins.

  • Peta

    Great idea! I love jQuery because of its ease of use and its overal api design, but on the other side I love MooTools because of it’s inheritance system its modular architecture … but I never had the idea to intermix the two. :-) Until now. Thanks Ryan!

  • http://fragged.org/ Dimitar Christoff

    very nicely done! I honestly don’t see a mootools dev doing this for the benefit of the dom manipulation speed through jquery, what with 1.3/2.0 coming soon and all. would be handy if I ever need to do any jquery dev though. ++

  • Matthew

    Interesting idea, because one framework doesn’t support what you need, add another, duplicate functions and increase load times. Or just learn how to do what you want with one framework.

  • Ryan Florence

    @Matthew: Because 24k uncompressed is soooo much. Please find the duplication between MooTools Native + Class and jQuery, then let me know. You obviously didn’t read the article. You can’t do what Class does in jQuery. If I were pulling in the entire library of scripts from MooTools, yeah, lots of duplication, and that’s stupid.

  • http://www.offroadcode.com Pete Duncanson

    Love the Mutator hack. Its not for me though, its like mixing French into your blog posts, confuses things and makes it harder to read/debug?

    A nice view into the MooTools world for jQuery users though and visa versa.

  • http://biberltd.com Can Berkol

    after a long time something worth to read.

  • http://www.nethzah.com/ Peter Benson

    Thanks for the post.

    Can’t this be done with jQuery alone?

    Thanks Peter

  • Ryan Florence

    @Peter: If you mean the spinner and the slideshow, of course. Both could be done with JS alone, you don’t even need either library. Can jQuery plugins implement or extend each other? No, that’s the point of the post.

  • http://dorkitude.com dorkitude

    Simply amazing work, Ryan.

    @Matthew: Actually, only one of the technologies discussed here is a framework. The other is a DOM toolkit.

  • http://clientcide.com Aaron Newton

    Spectacular work Ryan. Really great stuff.

  • http://mootools.com.br Rafael F P Viana

    Really nice work Ryan..keep it up

  • http://yumfo.com/?p=40 yumfo » Blog Archive » Object Oriented jQuery with MooTools aka the Yin Yang Framework

    [...] source:  Object Oriented jQuery with MooTools (Pigs take flight) [...]

  • http://thejavascriptblog.com Merrick Christensen

    You just saved me a ton of stress about every project I have that demands the use of “jQuery”. So cool. Thank you!

  • http://vombat.tumblr.com Anurag Mishra

    Just amazing work Ryan. It’s like having two cakes and eating them too.

  • Ryan Florence

    @Anurag – More like having a three course meal, but substituting one cake out for another! :)

  • http://clientcide.com Aaron Newton

    The cake is a lie.

  • http://www.burnsvillewebsites.com Burnsville Web Design

    My mind has been blown. Combining two libraries is genius. But what about load times to slow speed users?

  • Ryan Florence

    This came up earlier. MooTools is modular. I’ve only included the pieces required to write chunks of business logic into classes: Class, and Native. The uncompressed version is only 24k, YUI is 14.7k; gzipped it’s almost nothing. Considering jQuery 1.4 is over 150k uncompressed, I think you’re okay.

    There’s also no duplication (maybe a TINY bit, like jQuery.proxy and bind) but there’s not an ounce of code that has to do with elements, animation, ajax, etc. in the mootools portion. Though you could include them too if you wanted.

  • http://www.evanbot.com Evan Byrne

    This is incredibly useful. I prefer to use jQuery over Mootools, but for certain complex projects like desktop apps the structure of jQuery just doesn’t work very well. Thank you!

  • http://www.timnovinger.com Tim Novinger

    Ryan, I love what you’ve done here. Just a few days ago I ran into the issue of writing a modal popup script in jQuery, yet because I have a Mootools background, I wanted it to behave like a typical MT class.

    My solution was ok, but nothing as elegant as yours, nor as well functioning. At best I was able to make it “sort of” behave “classy” (which is as good as “truthy” is in JS).

    Only one question for you though. What happens when you DO have duplication (such as jQuery.proxy and .bind)? Which takes precedence over which? Is it based on which library was loaded first?

    I’d love to be able to .bind(this) in jQuery classes (hah, so awesome that I can say that), but will it be treated as an event handler instead?

    What happens in this scenario?

    Thanks again! MooQuery FTW!

  • http://davidwalsh.name David Walsh

    Ryan Florence FTW!

  • http://uxdriven.com Eric Clemmons

    It used to be mixing & matching frameworks was a no-no, but let’s face it: jQuery just isn’t meant for OOP-style programming in Javascript. Being able to take an existing Lightbox implementation and tweak options & a couple functions is immensely beneficial.

  • Ryan Florence

    @Tim

    What happens when you DO have duplication (such as jQuery.proxy and .bind)? Which takes precedence over which?

    Good question. Bind extends the Native Function directly, jquery.proxy extends the jquery object, so they don’t bump into each other, they just do the same thing (generally). Use either one.

    The only concern was having two functions that do the same thing, it’s just a waste of bytes, not a conflict. You’ll notice in my scripts I used bind all over the place.

  • http://www.codylindley.com Cody Lindley

    Outstanding work and very respectable perspective!

  • http://christiank.org/wp/2010/03/mixing-mootools-class-with-jquery/ Mixing MooTools class with jQuery « Thoughts, Codes and Games

    [...] http://ryanflorence.com/object-oriented-jquery-with-mootools-pigs-take-flight/ this is pretty [flippin'] awesome! I thing i know what im spending my weekend doing! Brilliant work [...]

  • http://nicbell.net Nic Bell

    ASP.net MVC2 framework uses jquery and this was really bugging me (I thought I’d have to change my coding style), awesome post you have saved me a lot of headaches :D :D David is right Ryan Florence FTW

  • Bruno

    How can I make this solution constantly updated?

    I think is better save each content (MooTools, jQuery and Mutator) in their own files.

    But, in what order I have to include in my page? jQuery, MooTools, Mutator? Mutator, jQuery, MooTools? Doesn’t matter the order…

    And, one more thing. This technique could, someday, fail? The Mutator implementation can not be used in a future release of one of the main frameworks?

  • http://www.chlab.ch/ Chris

    Great article, I like the idea. Wrote a short article about this on my blog: http://www.chlab.ch/blog/archives/javascript/object-oriented-jquery-mootools

    Cheers, Chris

  • FullContactCoder

    Really interesting read!

    What advantages would using a mix of MT and jQuery have over a pure YUI 3 implentation (which is sandboxed, fully modular, OO friendly, and now has all the great selector and chaining features of jQuery)?

  • Ryan Florence

    Haven’t used YUI3 to any extent to speak intelligently about it, but that’s never stopped me before.

    Using just YUI or just MooTools is nice because it’s all just one library instead of a funky hybrid (check the docs in one place).

    However, I see two advantages that moo4q has over YUI or 100% MooTools.

    1. The incredible popularity of jQuery, and by that I mean thousands of plugins and support
    2. Inherited projects usually use jQuery. By using moo4q I can write my new code with objects without the overhead of rewriting the typical mess that many jQuery users create (not hatin’, just sayin’) and it only costs ~8k gzipped.

    Of course, anybody who knows me knows I only use moo4q in cases such as #2. Moo4q is for jQuery users, they don’t have anything helping them with prototypal inheritance. MooTools, YUI, Dojo, etc. all have something.

  • http://softwareas.com/a-jquery-inheritance-experiment A jQuery Inheritance Experiment

    [...] that's not a good idea…I'm making jQuery into something it's not. So on IRC someone pointed me to this MooTools-jQuery article. I need to read that article. I also need to get into MooTools, which I think may be more [...]

blog comments powered by Disqus

Comments RSS