jQuery 1.4, MooTools 1.2 Compared
By Ryan Florence, published 2010-02-05
Part of the issue Migrated Articles From Original Site Structure..
Background
I recently read jQuery 1.4 Released: The 15 New Features you Must Know over at Nettuts and thought it would be interesting to compare the features of MooTools 1.2 to the new features in jQuery 1.4, released 18 months later.
I didn’t try hard to sound unbiased in this article. I prefer MooTools and it’s evident in the things I notice about jQuery when viewed through my biased eyes. However, please don’t read this as though I’m trying to convince you to switch from jQuery to Mootools, or that jQuery is inferior, or assume that you browse the web with IE, use Windows, drive a Ford, and drink Pepsi. I’m just sharing my thoughts as a mootools developer (I do use jquery on inherted projects)
Anyway, I’ve included a working code snippet for each feature using jsfiddle, save a few, so that you can goof with the code and see how awesome some of these new features in jQuery are, and how awesome some have always been in MooTools. Some of the embedded shells are acting funny so you may need to click the pencil button to really see what’s going on.
Enjoy!
1. Passing Attributes During Element Construction
jQuery 1.4
jQuery('<div/>', {
id: 'foo',
css: {
fontWeight: 700,
color: 'green'
},
click: function(){
alert('Foo has been clicked!');
}
});
MooTools (all versions)
new Element('div',{
id: 'foo',
styles: {
'font-weight': 700,
color: 'green'
},
events: {
'click': function(){
alert('Foo has been clicked!');
}
}
});
jQuery is looking a lot more moo-ish here by passing an object in rather than chaining everything.
jQuery Shell
MooTools Shell
2. Everything Until!
HTML
<ul id="fruit">
<li>Apple</li>
<li>Banana</li>
<li>Grape</li>
<li>Strawberry</li>
<li>Pear</li>
<li>Peach</li>
</ul>
jQuery 1.4
jQuery('ul#fruit li:contains(Apple)').nextUntil(':contains(Pear)');
// Selects Banana, Grape, Strawberry
MooTools 1.2 + Element.GetUntil Plugin
$$('ul li:contains(Apple)')[0].getAllNextUntil(':contains(Pear));
// more mooish
$('fruit').getElement('li:contains(Apple)').getAllNextUntil(':contains(Pear));
I thought these new until methods were interesting so I extended MooTools with similar functionality. See my post about Element.GetUntil.
jQuery
MooTools
3. Binding Multiple Events
jQuery 1.4
jQuery('#foo').bind({
click: function() {
// do something
},
mouseover: function() {
// do something
},
mouseout: function() {
// do something
}
});
MooTools (all versions)
$('foo').addEvents({
click: function(){
// do something
},
mouseenter: function(){
// do stuff a bit better than mouseover
},
mouseleave: function(){
// do stuff a bit better than mouseout
}
});
jQuery 1.4 again is looking a lot more like mootools, very nice because now you can store a handful of events in an object and toss them around.
Note: “MooTools features mouseenter and mouseleave because mouseover/mouseout sometimes just don’t not work as expected. Mouseenter only fires once you enter the element and does not fire again if your mouse crosses over children of the element.” moootools.net
jQuery
MooTools
4. Per Property Easing
jQuery 1.4
jQuery('#foo').animate({
left: 500,
top: [500, 'easeOutBounce']
}, 2000);
MooTools 1.2 + Fx.Transmorph Plugin
Fx.Transmorph is the same thing, different syntax:
$('foo').transmorph({height: 100, width: 100}, {width: 'bounce:out'});
MooTools 1.2 + Fx.Flux Plugin
Fx.Flux is unique in that it allows you to define ANY of the Fx.Morph instructions rather than just the transition. Yes, you can change the duration, transition, unit, etc.
$('foo').flux({
'top':[370, {
duration: 1500,
transition:'bounce:out'
}],
'left':[370, {
duration: 3000,
transition:'cubic:out'
}]
});
Just Mootools
You’d have to set up multiple Tween / Morph instances. However, this affords you control over every aspect of the tween (including events), not just the easing.
var foo = $('foo');
var fooFx1 = new Fx.Tween(foo,{
transition: 'elastic:out',
duration: 2000,
onStart: function(){
// do something fun
}
});
var fooFx2 = new Fx.Morph(foo,{
transition: 'sine:in:out',
duration: 500,
onComplete: function(){
// do something important
}
});
fooFx1.start('left',500);
fooFx2.start({'top':500, 'border':'10'});
So yeah, a lot of code. I think jQuery implemented something cool here and I certainly hope to see a version of Fx.Flux in the mootools forge. When we all start getting crazy with canvas and svg we’ll want leaner syntax for animation than the mootools code block above this, and more control than just the easing in jQuery. Oh, Fx.Flux, yes, that’s what I’m thinking of.
jQuery
MooTools Fx.Flux
5. Live Events
That’s just another name for event delegation. I’ve got an entire post on event delegation with mootools. Basic concept, instead of adding the same events (or as jquery calls it, binding events) on multiple elements, just add the event to a single parent element and delegate, or relay the event to the children. Then any dynamically added elements (inserted via ajax, or otherwise) will have the event.
jQuery 1.4
jQuery('ul#foo li').live('click', function(){
// do something with this
});
MooTools 1.2 + Element.Delegates
$('foo').addEvent('click:relay(li)');
Some very big differences in the two approaches here.
-
jQuery listens to the entire document for clicks. That feels a little, erm, less-awesome than it should be. That means every single click goes ahead and finds out if it was an
li
inside offoo
that was clicked. -
MooTools only listens to the element you tell it to.
MooTools is also more clear about what’s going on. You are adding the event to an element that will relay the event to it’s matched children. The identical code in MooTools to the jquery example would be:
document.addEvent('click:relay(ul#foo > li)');
But don’t ever do that if you don’t have to. Srsly d00d. Don’t get me wrong, event delegation is one of my favorite toys, so I’m very excited it’s part of jQuery now, just a bit disappointed I have to listen to the whole document, he’s going to be a busy man.
Note: Jquery kicks MooTools in the pants when it comes to delegating focusin
and focusout
. The current implementation of MooTools event delegation doesn’t support the focus
and blur
methods, but MooTools 2.0 will.
jQuery
MooTools
6. Controlling a Functions Context
In other words, what will this
be inside a function? Given this:
var app = {
config: {
clickMessage: 'Hi!'
},
clickHandler: function() {
alert(this.config.clickMessage);
}
};
// jQuery
jQuery('a').bind('click', app.clickHandler); // won't work, `this` is the clicked `a`
// Mootools
$$('a').addEvent('click', app.clickHandler); // same thing
When an anchor tag is clicked, we want to alert this.config.clickMessage
, or ‘Hi!’. Here are the solutions:
jQuery 1.4
jQuery('a').bind(
'click',
jQuery.proxy(app, 'clickHandler')
);
MooTools (all versions)
$$('a').addEvent(
'click',
app.clickHandler.bind(app)
);
This should have been in jQuery a long time ago. I have a hard time imagining writing a well structured javascript app without binding, or proxy-ing (?) as shown in this example. I think the MooTools syntax is easier as you don’t have to split up the object, just bind it. Things get a little confusing too with jQuery when you’ve got nested methods in an object:
var app = {
config: {
clickMessage: 'Hi!'
},
clickHandler: function() {
alert(this.config.clickMessage);
},
nested: {
keyupHandler: function() {
alert(this.config.clickMessage);
}
}
};
jQuery('a').bind({
'click': jQuery.proxy(app, 'clickHandler'),
'keyup': jQuery.proxy(app.nested.keyupHandler, app) // backwards?
}
// MooTools
$$('a').addEvents({
'click': app.clickHandler.bind(app)
'keyup': app.nested.keyupHandler.bind(app) // coherent
});
Seems really strange to swap the arguments around to accommodate a nested method. MooTools is much clearer here, just pass in the function as you would normally, then bind whatever you want.
jQuery
MooTools
7. Delay an animation queue
jQuery
$('#foo')
.animate({'width': 250})
.delay(500)
.fadeOut()
.delay(1000)
.fadeIn();
MooTools 1.2 with Chain.Wait
$('foo')
.tween('width', 250)
.pauseFx(500)
.fade('out')
.pauseFx(1000)
.fade('in');
jQuery
MooTools
8. Check if an element has something
jQuery 1.4
jQuery('div').has('ul');
MooTools 1.2
$('div').hasChild('ul');
9. Unwrap an element
jQuery 1.4
jQuery('p').unwrap();
MooTools 1.2
Nuthin’ baked in, but if you find yourself needing to unwrap elements often you could extend Element
to handle it.
// include this with your other classes and javascript
Element.implement({
unwrap: function(){
var parent = this.getParent();
parent.getChildren().inject(parent,'before');
parent.dispose();
return this;
}
});
// now you've got unwrap
$('p').unwrap();
Edit: Thanks to Chee Aun in the comments, he pointed out that I missed text nodes in my solution. Indeed. Check out his gist for a better solution. You would instead target an element to remove rather than a child to oust it’s parent.
jQuery
MooTools
10. Remove elements without deleting data
jQuery
foo.detach(); // Remove it from the DOM
// ... do stuff
foo.appendTo('body'); // Add it back to the DOM
MooTools (all versions)
foo.dispose(); // Remove from the DOM
// ... do stuff
foo.inject(document.body); // Add it back to the DOM
Again, this was one of those things that surprised me that jquery didn’t have, especially since jQuery’s focus is the DOM. It’s especially helpful when you’ve got some heavy duty dom manipulation going on: pull everything out, manipulate, the browser doesn’t try to redraw, inject it back into the DOM. Only redraws once.
11. Index enhancements
jQuery 1.4
jQuery('li').click(function(){
alert( jQuery(this).index() );
alert( jQuery(this).index('li.other') );
});
MooTools
var els = $$('li');
els.addEvent('click',function(){
alert(els.indexOf(this));
alert($$('li.other').indexOf(this));
});
// more common
$$('li').each(function(element, index){
element.addEvent('click',function(){
alert(index);
});
});
This is clearly more valuable to the jquery coding style. I’ve never used indexOf
in mootools, I’m usually in an each
loop.
jQuery
MooTools
12. DOM Manipulation accepts functions as arguments
jQuery 1.4
jQuery('li').html(function(i){
return 'Index of this list item: ' + i;
});
This is actually quite interesting, it’s geared a lot more toward the coding style of jQuery than mootools, but after talking to Thomas Aylott about it, we (read: he), came up with this …
MooTools 1.2 with Elements.setEach Plugin
$$('li').setEach('html',function(i){
return 'Index of this list item: ' + i;
});
// actually, there's a ton you can do with setEach
function replaceFooWithBar(string){
return String(string).replace(/\bfoo\b/g,'bar');
};
// just like jQuery
$$('a').setEach('href', function(currentHref, i){
return currentHref + '?foo=bar';
});
// or pass in an array of stuff and it'll do it all
// notice how `html` is accessible in the function
$$('a').setEach('html',[
"New foo HTML!",
function(html){ return html + ' appended moar foo!'; },
replaceFooWithBar,
function(html,i){ return html + ' Index is ' + i; }
]);
// or set multiple things, in this case, multiple styles
$$('a').setEachStyle({
'color': function(currentColor, i) {
i = i.toString(16);
return ['#', i, i, i].join('');
},
'background-color': function(currentColor, i) {
i = (15 - i).toString(16);
return ['#', i, i, i].join('');
}
});
Radical. Hats off to Thomas Aylott for this 30 minute script.
jQuery
MooTools
13. Determine the Type of Object (sort of)
jQuery 1.4
jQuery.isEmptyObject({}); // true
jQuery.isEmptyObject({foo:1}); // false
jQuery.isPlainObject({}); // true
jQuery.isPlainObject(window); // false
jQuery.isPlainObject(jQuery()); // false
I’m not entirely sure how helpful this is. Seems like if an object is empty when you iterate over it, nothing happens, is there a reason to check first? Also, why do I care if it’s {}
versus new Object()
. I’m genuinely curious, not bashing.
MooTools 1.2
Straight from Keetology: Up the Moo Herd V: Evident Utensil
// JavaScript's typeof operator..
typeof "a"; // 'string'
typeof 1; // 'number'
typeof {}; // 'object'
typeof []; // 'object'.. wait, what?
typeof /m/; // 'object' in some, 'function' in others..
typeof $('id'); // 'object'.. you're not helping!
// $type..
$type("a"); // 'string'
$type(1); // 'number'
$type({}); // 'object'
$type([]); // 'array'.. nice!
$type(/m/); // 'regexp'.. perfect!
$type($('id')); // 'element'.. now you're just showing off..
I know, different, but when I read the header this is the type of thing I expected.
14. Closest(…) Enhancements
Nothing in mootools like this. It seems really similar parentsUntil
.
15. New Events! focusin and focusout
As mentioned earlier, you can’t delegate the focus
and blur
methods directly; jQuery has created these two custom events to make it possible. Flippin’ awesome. MooTools 1.2 doesn’t have this ability, but MooTools 1.3 (due any day now) will have focus, blur, and every other event known to geek.
The Score
Already in MooTools 1.1 or 1.2
1, 3, 5, 6, 7, 8, 10, 11
Not in MooTools -core or -more
2, 4, 9, 12, 13, 14, 15
Available as a plugin in the MooTools forge
2, 4, 12
jQuery put the smack down
15 (But hey, 1.3 is coming!)
Conclusion
Obviously, the styles of jQuery v. MooTools are very different so these comparisons are always a bit funny since both frameworks approach javascript differently. I think the jQuery users out there are going to love some of these new features, especially the ones that mootools developers have been using for a while now.
JQuery continues to be a one stop shop for the DOM, packing in everything you could ever want to do to an element (1.4 is over 150k now uncompressed) while MooTools keeps making -core leaner and more modular (there’s even a server version with no browser or DOM stuff), and encouraging users to only pull in the classes and plugins they need. Potayto, potahto.
It’s interesting to note that some of jQuery’s stuff is starting to pass objects in as arguments rather than encouraging long chains. It may just become a bunch of your own Object Oriented Tools (YooTools, oh snap!) Seriously, the new jQuery is a winner, it’s exciting to visit websites that are using any of the great libraries out there, advancing the web experience.