Writing better javascript – Part 8
JavaScript event model
This is another practice I have adopted in my JavaScript development to enable the components I create be able to raise custom events and have an event model that will support consumers to be able to use them as needed. That said, please do let me know if this event model can be improved upon in anyway possible. Always open to comments and suggestions…
In the code illustration below, I am adding the ‘click’, ‘select’, ‘unselect’, ‘enable’ and ‘disable’ event support to my MapTool JavaScript component. If you have been building mapping application then it should be evident why I would need those events on my map tools. I create a private listeners object where I store the listeners for every supported event as properties with the same name as that of the event. That makes it a little easier to retrieve the array of listeners for a certain named event. I also throw an error if the user tries to add a listener to an event that has not been pre-defined. This is obviously optional and be handled a couple of different ways. If the user tries to add a listener to an event that has not been pre-defined, the add listener call can just be ignored or a new event with that name can be added to the listeners object (but I don’t have such requirements in my applications so I throw an error). I also use the code snippet from John Resig (jQuery’s father) to be able to remove items from an array. That function can also be added to the array prototype if you are comfortable with messing the Array prototype.
var MapTool = function(toolID, options) {
//private properties
var _listeners = new Object(); //Create an object that hold the arrays of listeners as its properties
//Predefine known events on the object – optional
_listeners.click = new Array();
_listeners.select = new Array();
_listeners.unselect = new Array();
_listeners.enable = new Array();
_listeners.disable = new Array();//Adding John Resig’s array remove function
_removeFromArray = function(array, from, to) {
var rest = array.slice((to || from) + 1 || array.length);
array.length = from < 0 ? array.length + from : from;
return array.push.apply(array, rest);
};
//var _raiseEvent = function(eventName, e) {
if (_listeners[eventName]) {
for (var i = 0; i < _listeners[eventName].length; i++) {
if (_listeners[eventName][i]) {
_listeners[eventName][i](e);
}
}
}
};var _clearListeners = function(eventName) {
if (_listeners[eventName]) {
_listeners[eventName] = new Array();
}
};return {
createListener: function(eventName) {
if (!_listeners[eventName]) {
_listeners[eventName] = new Array();
}
},addListener: function(eventName, listener) {
if(typeof(eventName) !== ‘string’ || typeof(listener) !== ‘function’) {
if (!_listeners[eventName]) {
throw “The event ‘” + eventName + “‘ is not supported by MapTool.”;
//_listeners[eventName] = new Array(); – this is another option if you don’t want to throw an error if the user tries to add a listener for an unknown event
}
return { name: eventName, index: _listeners[eventName].push(listener) };
}
else {
throw “Cannot add event listener. Invalid argument. A valid event name and listener function is required.”;
}
},removeListener: function(eventObj) {
if (eventObj && eventObj.name && (eventObj.index || eventObj.index === 0)) {
if (_listeners[eventObj.name]) {
_removeFromArray(_listeners[eventObj.index], index);
}
}
else {
throw “Cannot remove listener. Inavalid event listener object.”;
}
}
};
};
The above event model has been working pretty good for me and the requirements in my applications.
leave a comment