Example: Custom Event Bubbling and Behaviors

The Custom Event framework is one of the principle communication mechanisms in YUI. An object can be augmented with EventTarget, enabling it to be both a host and a target for custom events. Custom events fire from their host and optionally bubble up to one or more targets. This allows you to make the interesting moments of your applications broadly available within a module, within a set of modules, or throughout a complex interface populated with rich elements.

In this example, a simple custom event is illustrated: testEvent. This custom event is hosted on a Publisher object and bubbles up to a BubbleTarget object.

An illustration of the relationship between the custom event, its host, and its Bubble Target.

Like DOM events, custom event bubbling can be stopped with e.stopPropagation() and default behavior can be suppressed with e.preventDefault().

  1. Custom Event log messages will appear here.

Source Code

The full source code for this example follows. Read through the comments and code to get an understanding of how you can make use of custom events in your own application development.

// Bubbling events are added by the event-custom module.
YUI().use('event-custom', 'node', function (Y) {

    var logger          = Y.one("#log");
        stopCheckbox    = Y.one("#stopPropagation"),
        preventCheckbox = Y.one("#preventDefault");

    // We'll create two classes, one to fire the event, and another to be a
    // bubble target for the other.  All events from the Publisher class can
    // then be subscribed from either the Publisher instance or the BubbleTarget
    // instance that it's related to.
    function BubbleTarget() {
        Y.log("BubbleTarget constructor executed.");
    }

    function Publisher(bubbleTo) {
        Y.log("Publisher constructor executed.");

        this.init(bubbleTo); // see class prototype below
    }

    // Publishers need to add the provided target to their bubble chain with
    // `addTarget`. We'll do this, and publish an event, in an `init` method
    Publisher.prototype = {
        init: function (bubbleTo) {

            // `addTarget` is the EventTarget method to register new bubble
            // targets for this instance
            this.addTarget(bubbleTo);

            // It's only necessary to publish events with special configuration,
            // such as default, stop, or prevent behaviors.  You can always
            // fire any event name you wish, published or unpublished.
            this.publish("testEvent", {
                // Pass an event facade to subscribers so they can call
                // e.preventDefault() and other methods.
                emitFacade: true,

                // An event's default behavior is defined in defaultFn.  This
                // will execute unless a subscriber calls `e.preventDefault()`
                defaultFn: function () {
                    Y.log("defaultFn: testEvent's defaultFn executed.");
                },

                // You can react to subscribers preventing default behavior as
                // well, by defining a preventedFn.
                preventedFn: function () {
                    Y.log("preventedFn: A subscriber to testEvent called preventDefault().");
                },

                // If a subscriber calls `e.stopPropagation()`, the event won't
                // bubble any further, and the stoppedFn will be called if one
                // is defined.
                stoppedFn: function () {
                    Y.log("stoppedFn: A subscriber to testEvent called stopPropagation().");
                }
            });
        }
    };


    // To fire events or be a bubble target, augment a class with EventTarget
    Y.augment(Publisher, Y.EventTarget);
    Y.augment(BubbleTarget, Y.EventTarget);


    // SEE IT IN ACTION

    var bubbleTarget = new BubbleTarget();

    // You can subscribe to the "testEvent" from the BubbleTarget, even before
    // a Publisher is created
    bubbleTarget.subscribe("testEvent", function (e) {
        Y.log("testEvent fired on the BubbleTarget object.");
    });

    // Create a Publisher instance, and link it to our BubbleTarget
    var publisher = new Publisher(bubbleTarget);

    // We can also subscribe to the testEvent on the Publisher instance.
    publisher.on("testEvent", function (e) {
        Y.log("testEvent subscriber fired on the publisher object.");

        if (stopCheckbox.get("checked")) {
            e.stopPropagation();
        }
        
        if (preventCheckbox.get("checked")) {
            e.preventDefault();
        }
    });


    // Wire up the example button to fire the event from our publisher
    Y.one("#fire").on("click", function (e) {
        logger.empty(); // clear out the logger:

        publisher.fire("testEvent");
    });

    // A little supporting magic to output Y.log() statements to the screen
    Y.on("yui:log", function (e) {
        logger.append("<li>" + e.msg + "</li>");
    });

});