Events

Core.js provides several features for event driven development, including the Core.EventListenerList for managing multiple types of event handlers, and Core.method() as a convenient means of registering methods of object instances as event listeners.

Registering Event Listeners to Invoke Object Methods

Event-driven JavaScript objects typically provide the developer with the capability to register a Function to be notified when an event occurs. The event-producing object will provide methods to add and remove Functions to be notified of the event, and will invoke each of the registered functions when the event occurs, typically with the event as a parameter.

When an event handler Function that is a method of an object instance is invoked, its this-pointer will not be set to the object instance. It will be invoked as though it is a stand-alone function. For example, the following fragment of code which registers event listeners on an HTML element (hypothetically stored in this._buttonElement):

Core.extend({

    clickCount: 0,

    registerListeners: function() {
        this.buttonElement.addEventListener("click", this._processClick, false);
    },

    _processClick: function(e) {
        alert("This button has been clicked " + ++this.clickCount + " time(s)");
    }
});

Note: for the sake of simplicity, this example code is only compatible with browsers that support DOM events, i.e., not Internet Explorer.

In the above example, when processEvent() is invoked, the this-pointer will not reference the object instance. Thus the value of this.clickCount will be undefined, and the code will not behave as desired.

Core's Core.method() function can be used to solve this issue, by creating a closure which will invoke an event-listener method with the this-pointer set to the appropriate object instance.

The following update to the listener registration code will allow the _processEvent() method to be invoked on the correct object instance:

this.buttonElement.addEventListener("click", Core.method(this, this.processClick), false);

Equality Issues

It is important to understand that the following code will evaluate to false:

Core.method(this, _processClick) == Core.method(this, _processClick)

The reason this will evaluate to false is because each invocation of Core.method() will return a new Function instance. For example, the following code will not remove the listener we registered earlier:

this.buttonElement.removeEventListener("click", this._processClick, false);

In the case where we need to unregister an event listener created with Core.method(), it will be necessary to retain a reference to the Function returned by Core.method(). This Function should then be passed to both the registration and unregistration methods, e.g.:

Core.extend({

    clickCount: 0,
    _processClickRef: null,

    $construct: function() {
        this._processClickRef = Core.method(this, this._processClick);
    },

    registerListeners: function() {
        this.buttonElement.addEventListener("click", this._processClickRef, false);
    },

    unregisterListeners: function() {
        this.buttonElement.removeEventListener("click", this._processClickRef, false);
    },

    _processClick: function(e) {
        alert("This button has been clicked " + ++this.clickCount + " time(s)");
    }
});

Managing Event Listeners

The Core.EventListenerList object provides a convenient means for your event-producing objects to manage their registered listeners and dispatch events to them. Only a single Core.EventListenerList is necessary per event-producing object, even if multiple types of listeners may be registered.

The following example shows a Core.EventListenerList in use:

Core.extend({

    _listeners: null,

    $construct: function() {
        this._listeners = new Core.EventListenerList();
    },

    addFooListener(l) {
        this._listeners.addListener("foo", l);
    },

    foo() {
        this._listeners.fireEvent({source: this, type: "foo"});
    },

    removeFooListener(l) {
        this._listeners.removeListener("foo", l);
    }
});

The addListener() and removeListener() events are used to register and unregister listeners, with the first parameter indicating the type of event and the second providing the listener to (un)register. When fireEvent() is invoked, the provided event's type property will be used to determine which listeners to notify. The provided event will be dispatched to all registered listeners of that type.