Creating event driven JavaScript objects
Matt Dunn, UI Consultant 18 May 2009
In this article I will demonstrate an easy way to create your own event driven JavaScript objects that will fit into the standard DOM event model. This will allow you to use any library, such as jQuery, to attach to your events - this avoids having to implement bespoke non-standard event mechanisms.
The first thing you will need is the IXXUS.core.event.manager object (this can be found in the example zip file at the end of this article). This exposes addEventListener and removeEventListener methods that provide a standard DOM event interface. All objects that require events inherit from this object.
In this example, we will create a simple timer object that has start and stop methods. First we are going to create an object called eventDrivenTimer in the namespace example. We need to inherit from IXXUS.core.event.manager - you can do this anyway you like but here we are going to use the call method. The event manager constructor takes a list of events to register for the object. This provides a robust definition of events that will be available for an object.
Here we create the skeleton eventDrivenTimer object that defines the events and methods:
var example = {
eventDrivenTimer: function() {
// Inherit from IXXUS.core.event.manager and pass the available events:
IXXUS.core.event.manager.call(this,
"start",
"stop"
);
this.start = function() {}
this.stop = function() {}
}
}As we have inherited from the event.manager object, we also have addEventListener and removeEventLister methods that provide a standard event interface to any event wire-up code. Now let’s add some code to the start and stop methods:
var example = {
eventDrivenTimer: function() {
IXXUS.core.event.manager.call(this,
"start",
"stop",
);
var _self = this;
var _startTime;
// Start the timer
this.start = function() {
// Dispatch the "start" event
_self.dispatchEvent("start");
_startTime = new Date().getTime();
}
// Stop the timer
this.stop = function() {
_endTime = new Date().getTime();
// Dispatch the "stop" event
oEvent = _self.dispatchEvent(
"stop",
{
startTime: _startTime,
endTime: _endTime,
totalTime: (_endTime - _startTime)
}
);
}
}
};We now have an object that has “start” and “stop” events we can attach event handlers to. You can use any library or your own code that provides methods to attach to events. In this example we will be using jQuery to bind to these events.
The dispatch in the stop method shows an example of how to pass an event object that has context during the event lifecycle. In this case, we are creating an event object that contains the original start time, the end time and the calculated total elapsed time. This object is passed to an event handler in that standard way as shown below in the stop event handler.
// Create a new instance of eventDrivenTimer
var myEventDrivenTimer = new example.eventDrivenTimer();
// Attach to the "start" event
$(myEventDrivenTimer).bind("start",
function() {
alert("Timer started");
}
);
// Attach to the "stop" event
$(myEventDrivenTimer).bind("stop",
function(e) {
// We are using jQuery to bind to events – in this case we need to examine the original event that was fired using the originalEvent property
alert("Timer ended: " + e.originalEvent.totalTime);
}
);
// Start the timer
myEventDrivenTimer.start();
// Emulate a sleep of 3 seconds and then stop the timer
setTimeout(
function() {
myEventDrivenTimer.stop();
},
3000
);When the script is run, the stop event is called after 3 seconds. You should therefore see an alert from within the stop event handler with a total time of approximately 3 seconds.
Cancellable events
So far we have two events that will fire when the start and stop methods are called. You may have more complex requirements where you need to have some control over the behaviour of an object during the event lifecycle. To demonstrate a simple case for our timer object, we will add a reset method that will dispatch a corresponding event that can be cancelled.
We will first add a new reset method and event to the object:
var example = {
eventDrivenTimer: function() {
// Inherit from IXXUS.core.event.manager and pass the available events:
IXXUS.core.event.manager.call(this,
"start",
"stop"
"reset" // Add a new reset event to this object
);
// existing object code
// Allow the timer to be reset
this.reset = function() {
var currentTime = new Date().getTime();
// Dispatch the "reset" event. We also pass a 3rd parameter that indicates that this event is cancelable
var oEvent = _self.dispatchEvent(
"reset",
{
startTime: _startTime,
totalTime: (currentTime - _startTime)
},
true // Allow this event to be cancelled
);
// Now the event has returned we need to see if it was cancelled by the event handler
if(oEvent.returnValue) {
_startTime = new Date().getTime();
}
}
}
}We can now attach a handler to the reset method:
// Attach to the "reset" event
$(myEventDrivenTimer).bind("reset",
function(e) {
// If you do want to stop the reset event then use the preventDefault method in the event handler:
e.preventDefault();
}
);And finally, we will call the reset method after 1 second:
// Emulate a sleep of 1 second and try a reset
setTimeout(
function() {
myEventDrivenTimer.reset();
},
1000
);When the code is run now, the totalTime will still be approximately 3 seconds as the reset method is being cancelled by the preventDefault method. In other words, we are preventing the default behaviour of the reset method and therefore calling this method has no effect on the timing. If we remove the preventDefault method (or unbind the reset event) the total time will be 2 seconds.
Give it a go
This is a very simple example of an event driven object but provides a powerful mechanism for creating far more complex code. By implementing an event driven behaviour layer in a MVC pattern JavaScript library you can get a huge amount of code reuse across multiple projects and eases testing by allowing individual objects to be tested via unit tests or other testing strategies.
Give it a try by downloading the complete example source and have a play.





Comments
Be the first to comment.
Add your comment