This project is archived and is in readonly mode.

#789 ✓wontfix
Chris the Developer

Better event delegation

Reported by Chris the Developer | November 17th, 2009 @ 08:45 AM | in 2.0 (closed)

The current :relay(selector) method has some drawbacks, namely:
* You have to add an event for each selector that has a delegate, * both direct event attachment and event delegation are currently in the same method, * the :relay pseudo-selector is somewhat confusing to people new to event delegation, * while all delegates added with :relay may be for the same event, each requires that you add a new event to the container

I propose the (separate) method below as a replacement to the current event delegation:

Element.implement({
                  
    delegateEvent: function(type, delegates, prevent, propagate) {  
    
        //get stored delegates
        var key = type + '-delegates',
            stored = this.retrieve(key) || false;
        
        // if stored delegates; extend with
        // new delegates and return self.
        if (stored) {
            var delegates = $extend(stored, delegates);
            this.store(key, delegates);         
            return this;
        }
        else {
            this.store(key, delegates);
        }
    
        return this.addEvent(type, function(e) {            
            // Get target and set defaults
            var target = document.id(e.target),
                prevent = prevent || true,
                propagate = propagate || false
                delegates = this.retrieve(key); 
    
            // Cycle through delegates
            for (var selector in delegates) {
                if (target.match(selector)) {
                    
                    // If a selector matches then fiddle with the natural flow as required
                    if (prevent) e.preventDefault();
                    if (!propagate) e.stopPropagation();
                    
                    // Fire the method up...
                    if (delegates[selector].apply) return delegates[selector].apply(target, $A(arguments));
                }
            }
        });     
    }
    
});

With this new method you can:
* Attach apply any delegate to any selector within the same call, * normal event attachment and event delegation are logically split, * only the first call for an event will attach an event and any subsequent additions of delegates will simply be placed in a simple delegate registry - many delegates, many selectors, many calls to the delegateEvent method but still only 1 event!

Small use-case:

window.addEvent('domready', function() {

document.body.delegateEvent('click', {
    '#clickme1': function() {
        console.log('clickme1 clicked');
    },
    '#clickme2': function() {
        console.log('clickme2 clicked');
        document.body.delegateEvent('click', {
            '#clickme1': function() {
                console.log('new delegate!');
            }
        });
    },
    '#clickme3': function() {
        console.log('clickme3 clicked');
    }
});



});

While 2 calls were made to delevateEvent, only 1 event was fired. The second delegation simply overwrote delegate in the registry.

Comments and changes to this ticket

  • Christoph Pojer

    Christoph Pojer December 18th, 2009 @ 12:28 AM

    • State changed from “new” to “invalid”

    I think our decision is to go with the implementation we use currently in MooTools-More. However, feel free to release this as a plugin on the MooTools Forge ( http://mootools.net/forge ). If users like this solution more, we will most likely rethink our approach.

  • Chris the Developer

    Chris the Developer December 18th, 2009 @ 12:47 AM

    But the critical flaw in the current implementation is that for multiple children you still have multiple events, which can't be right! You suggestion is a good one (I am currently trying to get it in the right shape for Forge), but you have to agree that the current implementation isn't the best way of going about things (whether you like my implementation or not); Multiple events for multiple children, additional events for the same delegate on separate attachments, unintuitive pseudos-selector attachment...

    I'm not saying 'pick me!' - just putting forward a case that illustrates the shortcomings of the current implementation with suggestions on how to overcome those limitations...If you're going to take the time to implement this useful feature (event delegation in general) then I think it should be done right, and open to improvement where improvement is due.

  • Christoph Pojer

    Christoph Pojer December 18th, 2009 @ 12:53 AM

    • State changed from “invalid” to “open”
    • Assigned user set to “Valerio”

    I'll assign it to Valerio, please post the link to your plugin once its up, we'll discuss it :)

  • Chris the Developer
  • Chris the Developer
  • Arieh

    Arieh February 10th, 2010 @ 11:14 AM

    The only flaw i see in this implementation is that i can't delegate 2 different events to the same selector. this would seem like a small deal for small usage, but if the same element (eg:Document) is used by more than 1 plugin it might create a collision between neighboring widgets.

    That being said, I also find that using the delegate method (as suggested here), passing it a list of selectors and functions is much more intuitive and also Mootools-like than the current implementation (using :relay(selector).
    though i suppose the 2 syntax could easily live side-by-side

  • Chris the Developer

    Chris the Developer February 10th, 2010 @ 11:22 AM

    @Arieh - Good points, though I think the '2 different events to the same selector' is antithetical to event delegation... You can add different event types to the same selector, with a call to delegateEvent for each event type, but more than 1 of the same kind of event to the same selector doesn't strike me as an efficient use of event delegation.

    All that being said - my goal for this plugin is be promote the use of event delegation by making it easy, and plugging as many holes in the current implementation as possible. If I were new to MooTools, I would sooner use elem.delegateEvent({...}); than elem.addEvent('click:relay(...)', fn() {...}); but if others find it easier to make use of delegation with modifications to the plugin then I would be happy to support those modifications. :)

  • Chris the Developer

    Chris the Developer February 11th, 2010 @ 12:43 PM

    I have added a delegateEvents method to the mix. Check it out here.

  • Chris the Developer

    Chris the Developer March 9th, 2010 @ 11:36 AM

    Just an update on the plugin I posted on Forge; about 220 downloads in the month it's been there. Perhaps the demand might prompt an addition to core, or something... :)

  • csuwldcat

    csuwldcat January 26th, 2011 @ 08:03 PM

    • Milestone order changed from “0” to “0”

    Hey Chris,

    In my opinion, the drawbacks from having to do multiple events do not out weigh the benefits that you have with the events being separated. With the pseudo method we get a few things for free:

    1. separation and event encapsulation
    2. gives users experience and a living example of what our pseudos are all about
    3. separate events are crucial for streamlining multi-event custom events

    I do agree that delegation should be in Core. I use it in 99% of projects and it is a huge win for performance. Swiff out, Delegation in. Period.

  • ibolmo

    ibolmo January 19th, 2012 @ 08:34 AM

    • State changed from “open” to “wontfix”

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

Shared Ticket Bins

Referenced by

Pages