rangerer.at

YUI custom events

When finally being able to hook into the lifecycle of the gallery-node-accordion module, I wanted to use events that other parts of the code could subscribe to. That way I wouldn't need to hard-wire otherwise unrelated business logic.

My first approach was to fire custom events on the corresponding nodes of the accordion: * accordion root fires accordion:ready * accordion section fires accordion:item:open and accordion:item:close

That way I would be able to subscribe to those events via Y, the accordion root or the corresponding section. In order to benefit from event bubbling and be able to subscribe in other parts of the code (with the same Y object) I needed to publish the events for the corresponding nodes:

accordion.publish('accordion:ready', { fireOnce: true, emitFacade: true, broadcast: 1 });
accordion.all('.yui3-accordion-item').each(function(node) {
    node.publish('accordion:item:open', { emitFacade: true, broadcast: 1 });
    node.publish('accordion:item:close', { emitFacade: true, broadcast: 1 });
});

While I liked the concept and flexibility of firing those events on certain nodes it turned out that there are certain limitations to that approach.

YUI().use('node', function(Y) {
    Y.use('gallery-node-accordion', function() {
        accordion.fire('accordion:ready');
    });
    Y.use('node', function() {
        Y.on('accordion:ready', function(e) { ... }, '.yui3-accordion-item');
    });
    Y.use('io', function() {
        Y.on('accordion:ready', function(e) { ... }, '.yui3-accordion-item');
    });
});

In the above code the subscriber in Y.use('node') triggers, but the subscriber in Y.use('io') does not. While in theory both should trigger (broadcasting being enabled and both sharing the same Y instance), there seem to be certain limitations to broadcasting events fired from a node. These restrictions seem to be specific to certain modules and/or if they were already loaded when the event is fired, but I don't fully understand the design decision behind it.

The easy solution is to fire and subscribe to custom events on the Y instance rather than using the specific nodes. You will loose the ability to limit your event subscription to a specific accordion instance or item via generic accordion:* events, but using application specific events will make your code cleaner and easier to understand. So I introduced the events people:accordion:ready, people:section:open and people:section:close leading to the following code:

Y.use('gallery-node-accordion', function() {
    Y.publish('people:accordion:ready', { fireOnce: true });
    Y.publish('people:section:open');
    Y.publish('people:section:close');
    Y.fire('people:accordion:ready');
});
Y.use('node', function() {
    Y.on('people:accordion:ready', function(e) { ... });
});
Y.use('io', function() {
    Y.on('people:section:open', function(e) { ... });
    Y.on('people:section:close', function(e) { ... });
});