Events with MooTools - Element, Class, Delegation and Pseudos

Written By Arian Stolwijk, on Monday, March 28th 2011, 2:18pm

One of the most useful and common part of MooTools is its Events Type. There are two Event usages: Element and Class. Element.Events is probably the most known because that’s probably one of the first things you’ve used when you started using MooTools. Furthermore, MooTools More 1.3 Events.Pseudos has been introduced to give even more power and control over Events and with Event Delegation can give your page a massive performance boost. This blog post will give you a deeper insight into all components.

Element.Events

Element.Events represents DOM node event abstraction. The most simple example is, of course, adding a click event to a DOM Element:

For the sake of completeness I’ll explain this code step by step. First we grab the a element with the document.id method. Then we call the Element:addEvent method. The addEvent accepts two arguments: the event name, which is a string without the ‘on’ part (click, keydown, mouseover) and the callback, which is a function. This function will be called when the user clicks the element.

There are two interesting things you see there: first, the event argument of the callback function. This is an instance of the Event Type. This is a wrapper of the native event object so we do not have to worry about browser inconsistencies. In this case the stop method is called to stop all default behaviors, like following the link. The second interesting thing is that we can use the this variable in the event callback which refers to the clickme element.

Removing Events

Once you’ve added events to an Element, you might want to remove them some time later. Removing events is pretty straightforward. We have to store a reference to the function if we only want to remove this single event.

// store a reference to the callback
var myCallback = function(event){
    alert('hi');
};

// add the event
myElement.addEvent('click', myCallback);

// remove the event
myElement.removeEvent('click', myCallback);

You can remove all the click events when calling the removeEvents method.

myElement.removeEvents('click');

This is an interesting example what you can do when you only want to click an element twice:

Firing Events manually

It is possible to fire events manually with the fireEvent method. This is not a very common method but can be useful.

The following example is how you can call events you’ve added to an element manually:

It is important to note that the event callback of the clickme event checks if the event.stop method exists. When using fireEvent you’re not calling the callback through the DOM but directly with pure JavaScript. You can pass additional arguments as a second argument of the fireEvent, e.g:

clickme.fireEvent('click', [{stop: function(){}}, 'MooTools'], 500);

What am I doing here? The second argument of the fireEvent accepts a single argument or an array of arguments. In this case, I pass an array of arguments, which contains an Object as the first item in the array. The Object helps get rid of the if (event && event.stop) condition in the former example because it mimics the Event object. In the forge there is a more sophisticated Event.Mock.

Additionally, I’ve passed a third argument to fireEvent, 500. This third argument is used to delay the execution of the event callback.

You don’t necessarily have to use existing events like click or mouseenter. You can fire an event like grow as well. If you add an event grow to the element it will work fine:

clickme.addEvent('grow', function(height){
    this.tween('height', this.getStyle('height').toInt() + height);
});
clickme.fireEvent('grow', 30);

Clone Events

An even lesser known method is the cloneEvents method. This method can be used to clone the events from the first element into the other, for example, after using the clone method.

How Events Work Internally

The methods in Element.Events: addEvent, addEvents, removeEvent, removeEvents, fireEvent and cloneEvents, which are described above, are actually wrappers to native browser methods. Unfortunately those native methods are inconsistent across browsers. Most browsers use the W3C specification addEventListener except IE 8 and lower which uses the attachEvent method. A MooTools wrapper for the addEventListener is called addListener. This is a private method that will give us a cross-browser implementation for addEvent.

Another reason why we use our own methods is garbage collection. This happens when adding events to elements and the elements are removed from the DOM later. The events are still there, but the element is not; this might cause to memory leaks. Fortunately MooTools handles this problem and you don’t need to manually fix this issue.

Finally MooTools stores all the event callbacks using the store and retrieve methods. You could retrieve the object with all event callbacks using myElement.retrieve('events'). This is private and not recommended because it could break your code sooner or later. The reason why MooTools stores all the callbacks is so you can easily remove, fire, or clone them without passing a reference to the callbacks each time.

Event Bubbling

An interesting thing about the DOM is that elements can be nested. So what will happen if you add events to an element and to the element’s child element? What will happen is called ‘Event Bubbling’.

This is a quick example:

In this example there are two different elements, where the a element is nested into the div element. Each element has its own click event. Now when you click the a element, both events are fired, first the event of the a element, then the event of the div element. Also note that we cannot use the Event:stop or Event:stopPropagation methods anymore because this will block bubbling, we should use Event.preventDefault in this case.

This is actually nothing special of MooTools, but this is how it’s specified by the W3C. The W3C specification has a capture phase too, that will go the other way around. Unfortunately MooTools cannot support this because IE 8 and lower doesn’t support it. The three phases are clearly explained in this diagram

Event Delegation

Event Delegation is a technique that uses event bubbling and the event.target property. The target property is always the element which is clicked (or hovered when using mouse* events). What you can do is add an event to one of the parent elements, which will fire too because of the bubbling, and check of the target property satisfies your constrains, for example if it matches a CSS selector.

A very basic example of event delegation is:

This example is very simple, but it can get more difficult with more complex delegates. That’s why MooTools More has the Element.Delegation component. This component makes it easy to use delegation.

This example uses Delegation. You can see that instead of using just click, click:relay(tr td.click) is used. With the Event Pseudo (will be explained later) relay we tell MooTools to check if the target or one of its parents matches the passed selector tr td.click. If the element is matched the callback gets fired.

Event Delegation has some important advantages. It especially improves performance. Imagine a page with hundreds of links or table rows. Instead of adding an event to every element, you can delegate the parent element by adding only one event. Secondly Event Delegation is useful when working with dynamic pages. In case you have the same large table which will update regularly by XHR Requests, you don’t have to add new events to the rows because the rows are added into the table which has the event. The click event will bubble up to the table element and when the new row is matched against the given selector the callback function is fired. This can save you a serious amount of code and logic.

Class Events

Now we have seen how Element Events work, we’ll explore the Class Events. Class Events brings similar methods like addEvent, addEvents, removeEvent, removeEvents and fireEvent to MooTools Class.

First an example from the docs to illustrate how it works:

var Widget = new Class({
    Implements: Events,
    initialize: function(element){
        // …
    },
    complete: function(){
        this.fireEvent('complete');
    }
});

var myWidget = new Widget();
myWidget.addEvent('complete', myFunction);

So what does this do? First it implements the Events mixin. The Implements mutator will copy the methods of the Events mixin into the Widget Class. In the complete method it uses the fireEvent to fire all the added ‘complete’ events. Once the Widget class is instantiated the example uses the addEvent method to add an event ‘complete’ with the callback myFunction.

When you want to pass arguments to the callback function, you can use the second argument of the fireEvent method, which accepts any value for a single argument or an array for multiple arguments, just like Element:fireEvent.

addEvent Internal argument

This is a fairly unknown but useful feature. The third argument of the addEvent method is called the internal argument. This argument can be set to true when you do not want that the event can be removed.

Imagine you’re building a datepicker class. You split it up in two classes, a Picker class and a DatePicker class. The Picker Class is a generic picker which could be used for a ColorPicker or whatever picker too. It has an open method which opens the picker and fires the ‘open’ event. Now the DatePicker has to do some extra things on open. Now it could overwrite the ‘open’ method, but it can add an ‘open’ event too. Though you do not want that the user can remove the event when using myDatePicker.removeEvents('open'). In this case you set the internal argument to true.

Adding Events with Class Options

If you’ve used Fx or Request you definitely have used this: events as options when instantiating a Class. This are functions which have property names like onEventname where Eventname (the first character should be uppercase) is the actual eventname and is prefixed by on.

new Fx({
    onComplete: function(){
        // Will get fired when the Fx is complete
    },
    onPause: function(){
        // The Fx is paused
    }
});

The setOptions method of the Options mixin will automatically detect this and use the addEvent method.

var myWidget = new Class({
    Implements: Options,
    options: { // Default options and events
        onComplete: function(){
            // Will get fired when the class is complete
        },
        onPause: function(){
            // The class is paused
        }
    },
    initialize: function(options){
        // use the setOption method 
        this.setOptions(options);
    }
});

Events Pseudos

We’ve seen already a glimpse of Events Pseudos when using Event Delegation. This is a new component in MooTools More 1.3. It allows you to use event names as CSS Pseudo Selectors, thus event:pseudo(value).

A quick example how to use the :once pseudo:

Behind the scenes

Because the Element and Class methods are so similar, Events Pseudos is actually implemented once for both. It is totally abstracted to a single (private) function Events.Pseudos. It returns an object with modified addEvent and removeEvent methods. These methods are implemented into Events and Element, so it monkey patches the old ones.

When using one of the methods it will detect if the eventname is a pseudo eventname, if that’s the case the eventname is parsed by Slick.Parse to an object. Finally it will add an event to the Class or Element with a custom callback. That callback fires the corresponding pseudo function. That pseudo function is actually a function in between that can do some extra stuff.

Event Pseudo Functions

The ‘function in between that can do some stuff’ is actually the power of Events Pseudos. Like the :once pseudo function it will fire the passed callback function and remove itself, so the callback function can only be fired once. Another existing pseudo function for elements is :keys which waits till all passed keys are pressed until firing the callback. These psuedo functions can be defined by Events.definePseudo or Event.definePseudo. In MooTools More 1.3.1.1 the :pause and :throttle events were added.

At this point we come back at Event Delegation, because the :relay(selector) is a Pseudo Function too! That pseudo function basically uses Slick.match to check if the element matches the selector and should fire the callback function.

Creating your own Pseudo Functions

You can define your own pseudo functions. This can be done with the Events.definePseudo or Event.definePseudo functions. The first one for Class Events, the second one for Element Events.

This is an example how to create a pseudo event which only allows you to click an element with the right mousebutton based on the mousedown event:

This is a fairly simple example, but it is clear that you can do a lot of things with it while keeping your code clean and simple to read.

Another example that counts the number of clicks:

You can see that var event = args[0]; is used. The args argument is the argument that keeps the usual arguments passed into a normal event callback. Since we’re working with Element Events it has only one, so args[0] is the Event instance. Of course this argument can be different when working with Class events or when using the fireEvent method with multiple arguments.

The split argument contains an object with the parsed event name: event:pseudo(value). You can use the split.value property for example if you use the ( and ) for passing an extra value (like :relay(selector) for Event Delegation). The split.original property contains the whole string, so event:pseudo(value) which can be used to remove the event in the pseudo function, which is actually what the :once pseudo function does.

Finally we use fn.apply(this, args) to call the callback function (which highligts the a element). We use apply to be sure that this refers to the element and it is called with the same arguments as usually. You can do something else of course, but in most cases you want to use fn.apply(this, args).

Conclusion

Events are very powerful and you almost cannot write MooTools code without using Events. For the DOM, MooTools saves you a lot of pain and brings you the easy cross-browser API. Because of the nature of the DOM, it brings Event Bubbling which can be used for Event Delegation which can bring you several advantages. A unique part of MooTools is its Class, which brings the same API of DOM Events to Class. To give you even more power to Events, Events Pseudos can be used.

Quick tip

Also have a look at Company by Keeto or the PubSub pattern with the MooTools-Channels as MooTools implementation.

15 Responses to “Events with MooTools - Element, Class, Delegation and Pseudos”

  1. frostbytten says:

    Another quick tip, unless I am mistaken: In order for ‘change’ to fire for IE on radio/checkboxes you need the following.

    element.addEvents({‘change’: doSomething, ‘click’: function() { this.blur(); }});

  2. vedam says:

    Essentially usefull. Thanx a lot for this.

  3. mua sam vui says:

    thansk for this ! i am having intend to use it for my website

  4. Houston SQL Server Developer says:

    When reading through this article it is a bit confusing when references are made to code that does not yet show in an example. In particular, within the early section entitled “Element.Events”, the second paragraph under the screen-print includes a sentence that states: “In this case the stop method is called to stop all default behaviors, like following the link.”…

    Well, that discussion talks about a “stop method” which is not shown or otherwise introduced in any code thus far displayed. The method you refer to shows only later… you first are able to see that code in the “Removing Events” discussion in the JSFiddle gizmo at the end of that next section.

    Although this may sound trivial, it is the small oversights like this that will frustrate newcomers to MooTools. I have encountered all sorts of things like this in the MooTools mootorials and help pages, and it nearly convinced me to pass MooTools up in favor of a competing approach. Accurate documentation is just as important as the underlying technology if a framework is to be a truly useful framework.

    I was also comparing the Events discussion in this blog entry to what shows on the MooTorial site at: http://mootorial.com/wiki/mootorial/02-class/01-class.extras

    After doing so, my level of confusion just increases. That mootorial page even states: “In MooTools 1.2, Events starting with ‘on’ are still supported in all methods and are converted to their representation without ‘on’ (e.g. ‘onComplete’ becomes ‘complete’). If you reference the ‘on’ version (i.e. ‘onComplete’) it will still work, but this is deprecated.”…

    But yet, I see the “on” prefix used immediately thereafter in examples; immediately after the statement that said “on” is deprecated, etc. Oh please… please… just bring some consistency to all this documentation and show only the preferred way to implement events so that I do not accidentally choose a way that no longer works with future versions.

    I am trying to fully embrace MooTools, and I think it has a lot of potential for my needs; but, I am also finding inconsistencies and a lack of clarity in documentation and discussions are adding much more time to the learning curve and thus slowing my adoption of the framework. It would perhaps help quite a bit if you had your documents reviewed by someone not as familiar with the framework so that those things non-obvious (to outsiders) could be identified and corrected. Many of the issues revolve around ambiguity introduced by grammatical constructs that are not as precise as they could be - something a technical-editor (person) could help clear up. Just my thoughts.

  5. Arian says:

    The goal of this post was to tell more about events and assumes you know the basics a little. However you’re right about the .stop which I forgot. I updated the jsfiddle.

    Note that those are not screenprints, but embedded jsfiddles so you can play with the code yourself, run it and see what it does. I believe I added enough examples which are explained in the surrounding text.

    What you’re saying about the on part is that it is inconsistent in the MooToorial, is something that might be true, however the MooToorial is a great resource with a lot of info, but is a little outdated and written for mootools 1.2. Also note that this about Class Events. To explain it once more:

    it is onComplete when it’s used in the options property with the Options mixin. For everything it’s just complete. The examples in the MooToorial are wrong because they’re outdated. Though it is a Wiki and you can fix it. Besides the MooToorial is not an official MooTools resource like the docs. Furthermore we’re always considering and accepting changes to the docs, just look at how many little changes we made since 1.3.

    So what I tried with the post is to explain more about events than the docs could do. And what you could do is to report inconsistencies at lighthouse, ask questions at the google users group, come talk at IRC, but don’t get angry alone and wast many hours, but rather ask so you’ll learn new stuff and we might see how we could improve our docs or where we should write about in the next blog post.

  6. Tim says:

    First of all, thanks for the great tutorial. This tutorial should help a lot of beginners. So all guys begging for help with Events handling: read this blogpost and play with the jsFiddle widgets (examples) made by Arian! It’s a real help trying and playing with it in a practical way, than just read about the events.

    @Houston SQL Server Developer You’ve got a few points here, but looking 2 years back from now, there’s a huge growth and improvement at the Mootools side of the line. It’s not really fair to blame Mootools for all inconsistencies of documentation, seeing that they put all there energy and free time into improvements, fixes, new documentation and plugins for the framework. They even kindly ask the community to back them up and help them if there are inconsistencies. They do anything for the framework to make the whole package a better present for the community.

    Mootools was like a cow 2 years ago, but from the sideline I see the framework rapidly growing to a cow with a ribbon that can give whipped cream directly to the community :-)

    Thanks again for brightening up Event handling!

  7. Tim says:

    Very usefull! thanks for this blogpost :-)

  8. Houston SQL Server Developer says:

    Adrian, I appreciate your feedback. And, I may just take you up on the ability to edit the Wiki pages and/or report inconsistencies I find. I am undertaking the development of a component-set of sorts, which is making very heavy use of the pseudo-OOP Class-abilities that MooTools is providing. In fact, that is nearly all I am using from MooTools currently… I just needed a nice way to treat JavaScript as sorta-OO since I could not otherwise deal with it (I am coming at this from the perspective of a Delphi developer where I have strong typing in the language and all the encapsulation features, etc of real OOP).

    So, I definitely appreciate the work you and your team have done on MooTools, for the little parts of it I am using. And, I must say, the Class constructs are working pretty well for me so far. I am just getting into implementing my events-handling layer, and I hope to leverage what MooTools has to offer. No matter what, it has really helped me out and I am making great progress with JavaScript development that I thought would have been nearly impossible just a month or so ago. Regards.

  9. Alvin Crespo says:

    Great article! Today I thought I’d learn a bit more about MooTools and understanding events is a great way to learn a framework, and I have do have to say it was extremely useful to implement MT’s way of adding/removing events, delegating and stoping default and propagation up the DOM.

    After reading this article to understand more behind the scenes, I can definitely say I am pleased to have started learning this framework.

  10. Lino says:

    The event bubbing is very hard for me :). But now I know what is it. Great article on events.

  11. Edu says:

    Where can I find all the available types of events for MooTools (like mouseenter, click, etc? I can’t find a list of those in this reference and I am quite new to Javascript but I just figured MooTools doesn’t use the same names…

  12. Webdesigner says:

    very useful! just what i was searching for.

  13. Ian says:

    Thanks for the great post!

    Got a question though… is there a way to get the returned value of the callback when using fireEvent?

  14. Jonathan says:

    Thanks for writing this! I definitely learned a few things! The more I use mootools the more I notice consistency in the way things are done & I really appreciate the effort that goes into making it that way. It makes my life easier :)

  15. Web We Do says:

    Took me a while to get my head around it but after reading through it a few times I have it now.