ObjectHandler IDL Like
/**
* interface ObjectHandler implements EventListener {
* void handleEvent(in Event evt);
* void remitEvent(in Event evt);
* attribute Object events;
* };
*
* @link http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventListener
*/
var ObjectHandler = {
handleEvent: function handleEvent(e) {
var
events = this.events,
type = e.type
;
if (events.hasOwnProperty(type)) {
events[type].call(this, e);
}
},
// it could be called removeEvent too, as you wish
remitEvent: function cancelEvent(e) {
e.currentTarget.removeEventListener(
e.type, this, e.eventPhase === e.CAPTURING_PHASE
);
}
};
Above object is an Abstract implementation. In JavaScript we can borrow methods so whatever "class" would like to implement above behavior should extend the object or borrow those two methods and specify an events property.
Examples
Let's say we have a UI Class which aim is to react to certain events ... let's call it button:
function UIButton(parentNode) {
// add the node
this.el = parentNode.appendChild(
parentNode.ownerDocument.createElement("button")
);
// add quickly events to the node
for (var key in this.events) {
// add the listener ...
// NOTE: nothing to bind, no duplicated entries either
this.el.addEventListener(key, this, false);
}
}
// extends the class
Object.defineProperties(
UIButton.prototype,
{
// implements ObjectHandler
handleEvent: {value: ObjectHandler.handleEvent},
remitEvent: {value: ObjectHandler.remitEvent},
events: {value: {
// only once thanks to remitEvent
click: function (evt) {
alert("only once");
this.remitEvent(evt);
},
// some decoration on actions
mousedown: function (evt) {
this.el.style.border = "1px solid black";
},
mouseup: function (evt) {
this.el.style.border = null;
},
// recicle mouseup behavior
mouseout: function (evt) {
this.events.mouseup.call(this, evt);
}
}}
}
);
// example on window load
this.onload = function () {
new UIButton(document.body);
};
Got it? Events property is simply an object shared through the prototype with properties name that are exactly the equivalent of DOM events name ... easy!
Pros
- memory safe, nothing to bind, trap as bound, and stuff like this ... all instances will share same logic and bind is not even needed, aka: problem solved
- events are defined in a single place, easy to find, maintain, extend
- events are recycled, as it was as example for mouseout, though the instance
- most UI frameworks need the instance rather than the DOM node, to behave accordingly
- ObjectHandler can be used same way for "ObjectTarget", those listeners/able JS implementations, in order to uniform an event driven approach
- no risk to bind twice, no risk to remove the wrong callback, bound or not, it's the instance that makes the deal, not inline functions so it's less error prone
- performances in therms of both memory footprint and operation per each instance ... once again, no need to bind all methods to the instance itself ... what is in events is basically bound automatically through a single entry point, defined in ObjectHandler, for every single use case
I totally agree.
ReplyDeleteI've actually been doing something similar ever since I learned that add listener accepted an object.
Also, Apple's example code always use this approach.
I am the one that posted this technique in Ajaxian but for some reason never pushed for this technique based UI frameworks that much ... the last advantage is even the ability, if necessary, to swap realtime behaviors ( state machines ) ..... something that would be a complete mess with bound functions all over the place.
ReplyDeleteI like it! I have never used binding in my code as I never saw the need for it if you program carefully. In fact, except for highfalutin so-called 'experts' using it, it can repidly confuse and discourage new and welcome programmers to the field.
ReplyDelete