Event Delegation with MooTools

By  on  

Events play a huge role in JavaScript. I can't name one website I've created in the past two years that hasn't used JavaScript event handling on some level. Ask yourself: how often do I inject elements into the DOM and not add an event to them? For me it's very rare. For this reason I'm proud and excited for the release of MooTools 1.2.4's Event.Delegation code.

WTF is Event Delegation?

Event delegation is the process of assigning an event listener to a parent for all of its children instead of assigning the same event to every child.

Some Sample HTML

<ul id="link-list">
	<li><a href="https://davidwalsh.name">David Walsh Blog Link 1</a></li>
	<li><a href="https://davidwalsh.name">David Walsh Blog Link 2</a></li>
	<li><a href="https://davidwalsh.name">David Walsh Blog Link 3</a></li>
</ul>

A list with 3 list item elements which contain a link. For the sake of my example, this list will have list items added to it and we want an alert to pop up any time a link within the list is clicked.

The MooTools JavaScript Event Delegation Syntax

window.addEvent('domready',function() {
	/* delegate */
	document.id('link-list').addEvent('click:relay(a)', function(e){
		e.stop();
		alert('you clicked a link!');
	});
	/* 
		Add link to show event delegation works!
		Notice how we haven't assigned an event to this specific element.
		We already added the event to the list element itself
	*/
	document.id('add-link').addEvent('click',function() {
		var li = new Element('li').inject('link-list');
		var link = new Element('a',{ text:'David Walsh Blog', href:'https://davidwalsh.name'}).inject(li);
	});
});

All you need to do is add :relay to the parent selector and place the "children" match inside the relay pseudo selector. You'll probably question how :relay works because the ":" syntax is used for pseudo selectors. The Element.Delegation JavaScript download overwrites the addEvent, removeEvent, and fireEvent methods to accommodate for the :relay syntax.

Event Delegation Replaces...

var links = document.id('link-list').getElements('li');
links.each(function(link) {
	link.addEvent('click',function() {
		//assign actions here
	});
})

Why collect and iterate through elements to add events when you can simply use event delegation?

Event delegation is a great way to avoid repeating the same event assignments for elements within a parent element, especially when you are adding elements into the page dynamically. If you've not upgraded to MooTools 1.2.4 yet, I hope this is just the kick in the pants you need!

Recent Features

  • By
    Regular Expressions for the Rest of Us

    Sooner or later you'll run across a regular expression. With their cryptic syntax, confusing documentation and massive learning curve, most developers settle for copying and pasting them from StackOverflow and hoping they work. But what if you could decode regular expressions and harness their power? In...

  • By
    How to Create a RetroPie on Raspberry Pi &#8211; Graphical Guide

    Today we get to play amazing games on our super powered game consoles, PCs, VR headsets, and even mobile devices.  While I enjoy playing new games these days, I do long for the retro gaming systems I had when I was a kid: the original Nintendo...

Incredible Demos

  • By
    MooTools onLoad SmoothScrolling

    SmoothScroll is a fantastic MooTools plugin but smooth scrolling only occurs when the anchor is on the same page. Making SmoothScroll work across pages is as easy as a few extra line of MooTools and a querystring variable. The MooTools / PHP Of course, this is a...

  • By
    CSS Counters

    Counters.  They were a staple of the Geocities / early web scene that many of us "older" developers grew up with;  a feature then, the butt of web jokes now.  CSS has implemented its own type of counter, one more sane and straight-forward than the ole...

Discussion

  1. Event delegation is the shizzle, but I’m not a fan of how it’s done in MooTools. It’s great that it’s in there, but I feel that the functionality should be contained in its own method.

    I get that both assign events to the same element, but click:relay(selector) is not the most intuitive approach. I’m more for passing an object literal with the selector as key and the method as value, kind of like addEvents()…

  2. @Chris the Developer: Fair enough — let me know if you decide to create your own version. Our ears are always open! Thanks!

  3. I forgot all about this being added. Great stuff. Now when I pull html back from a request and I need to add events to it, no need to do collection.removeEvent collection.addEvent

  4. Element.implement({
    	delegateEvent: function(type, rules, prevent, propagate){
    		return this.addEvent(type, function(e) {
    			var event = new Event(e);
    			var target = document.id(event.target);
    			var prevent = prevent || false;
    			var propagate = propagate || true; 
    
    			for (var selector in rules) {
    				if (target.match(selector)) {
    					if (prevent) event.preventDefault();
    					if (!propagate) event.stopPropagation();
    					if (rules[selector].apply) return rules[selector].apply(target, $A(arguments));
    				}
    			}
    		});
    	}
    });
  5. Good grief…some mistakes in that one though. Like the 4 separate var statements and the new Event(e) nonsense…But you get the idea! :)

  6. Writing a function for this like Chris_the_Developer proposed is IMHO more intuitive and easier to handle.
    I didn’t really got the point with the “:relay” pseudoselector…

  7. Also, just to be clear, my approach allows you to delegate different functions for different selector matches, in the same setup:

    document.id('container').delegateEvent('click', {
    '.headings': function() {
    console.log('heading clicked');
    },
    '.intro': function() {
    console.log('intro clicked');
    }
    });
    

    Which means you can have 2 behaviors on 1 event. I don’t see how that’s possible with the relay event…I imagine you would have to add relay twice, which means 2 events for 2 behaviors.

  8. is this similar to the infamous .live() in jquery? have you also seen this implementation:

    http://www.k1der.net/country/mootools/live-events-demo.html

  9. @Dimitar Christoff: Yes.

    @Chris the Developer: Not bad! I’ll pass this onto the team and see what they have to say!

  10. jay

    So… what if you want to do click:relay( ‘a specific element, eg an input button – not a submit btn, but an input btn’ )

    in the above scenario what would ‘x’ be in click:relay(‘x’) ?

  11. @jay – click:relay(input[type=button])

  12. Chris’s method is cleaner, more powerful, and more intuitive.
    Much more “moootools”.

    Any reason why it didn’t fly? Delegation is now in the core, at v1.4

  13. GarciaWebDev

    And how do you fire that event with fireEvent() ? I can’t get it to work!

  14. GarciaWebDev

    Ok, got it:


    fireEvent('change:relay(element)')

    But I noticed that this now refers to the parent element… How can I get the child element reference inside the callback?

  15. Your code

    var links = document.id('link-list').getElements('li');
    links.each(function(link) {
      link.addEvent('click',function() {
        //assign actions here
      });
    })
    

    is a little over complicated – it could easily be replaced with

    var links = document.id('link-list').getElements('li');
    links.addEvent('click',function() {
        //assign actions here
    });
    

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!