In one of the former posts, I’ve mentioned that we were going to create a simple mobile chat application. In order to achive this, we need to add several modules to our o2.js framework, one of which is a cross-browser Event Handler.
Creating a cross-browser Event Handler is pretty straightforward, once you do proper object detection.
You can click here, to get the source code of this tutorial (5Kb zipped archive) »»
Adding an Event Listener
/**
* @function {static} o2.EventHandler.addEventListener
*
* <p>Adds a new event listener to the DOM Node.</p>
*
* @param {DomNode} obj - the object the evet shall be attached.
* @param {String} evt - the name of the event (like "click", "mousemove"...)
* @param {Function} fn - a reference to the on[event] callback action.
*/
addEventListener: function(obj, evt, fn) {
if(!obj) {
return;
}
if(obj.addEventListener) {
o2.EventHandler.addEventListener = function(obj, evt, fn) {
// "false" is for not to use event capturing. Event capturing
// is not very useful, since its implementation vastly deviates
// among different browser vendors.
// See: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow
obj.addEventListener(evt, fn, false);
};
o2.EventHandler.addEventListener(obj, evt, fn);
return;
}
if(obj.attachEvent) {
o2.EventHandler.addEventListener = function(obj, evt, fn) {
var onEvent = ['on',evt].join('');
obj.attachEvent(onEvent, fn);
};
o2.EventHandler.addEventListener(obj, evt, fn);
return;
}
o2.EventHandler.addEventListener = function(obj, evt, fn) {
var onEvent = ['on',evt].join('');
obj[onEvent] = fn;
};
o2.EventHandler.addEventListener(obj, evt, fn);
},
Removing an Event Listener
Removing an event handler is equally easy; the only thing too keep in mid is to cache a reference to the original event handling function, because we’ll need it when removing the listener.
/**
* @function {static} o2.EventHandler.removeEventListener
*
* <p>Removes an already-added new event listener from the DOM Node.</p>
*
* @param {DomNode} obj - the object the evet shall be removed.
* @param {String} evt - the name of the event (like "click", "mousemove"...)
* @param {Function} fn - a reference to the on[event] callback action.
*/
removeEventListener: function(obj, evt, fn) {
if(obj.removeEventListener) {
o2.EventHandler.removeEventListener = function(obj, evt, fn) {
obj.removeEventListener(evt, fn, false);
};
o2.EventHandler.removeEventListener(obj, evt, fn);
return;
}
if(obj.detachEvent) {
o2.EventHandler.removeEventListener = function(obj, evt, fn) {
var onEvent = ['on',evt].join('');
obj.detachEvent(onEvent, fn);
};
o2.EventHandler.removeEventListener(obj, evt, fn);
return;
}
o2.EventHandler.removeEventListener = function(obj, evt, fn) {
var onEvent = ['on',evt].join('');
obj[onEvent] = o2.nill;
};
o2.EventHandler.removeEventListener(obj, evt, fn);
},
Getting the Target of the Event
An important thing to consider is that any event propagates upwards in the DOM hierarchy, unless you stop the propagation explicitly (we’ll come to that in a second).
The target of the event is the initial DOM Node that the event starts propagating.
/**
* @function {static} o2.EventHandler.getTarget
*
* <p>Gets the originating source of the event.</p>
*
* @param {Event} evt - the actual <code>DOM Event</code> object used internally
* in {@link o2.EventHandler.addEventListener}
* @return the actual DOM target of the event object.
*/
getTarget: function(evt) {
var target = window.event ?
o2.EventHandler.getTarget = function() {
return window.event.srcElement;
} :
o2.EventHandler.getTarget = function(e) {
return e ? e.target : null;
};
return o2.EventHandler.getTarget(evt);
},
We’ll sometimes need to get the original DOM “event object”; here’s how:
/**
* @function {static} o2.EventHandler.getEventObject
*
* <p>Gets the actual event object.</p>
*
* @param {Event} evt - the actual <code>DOM Event</code> object used internally
* in {@link o2.EventHandler.addEventListener}
* @return the actual <code>DOM Event</code> object.
*/
getEventObject: function(evt) {
o2.EventHandler.getEventObject = window.event ? function() {
return window.event;
}: function(e) {
return e;
};
return o2.EventHandler.getEventObject(evt);
},
Stop Propagation
To stop the event propagation that we’ve mentioned earlier, we need to use cancelBubble for MSIE, and stopPropagation() for the standard-compliant browsers:
/**
* @function {static} o2.EventHandler.stopPropagation
*
* <p>Stops the propagation of the event upwards in the DOM hierarchy.</p>
* <p>See {@link http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow}
* for details.</p>
*
* @param {Event} evt - the actual <code>DOM Event</code> object used internally
* in {@link o2.EventHandler.addEventListener}
*/
stopPropagation: function(evt) {
o2.EventHandler.stopPropagation = window.event ? function() {
window.event.cancelBubble = true;
} : function(e) {
if(!e) {
return;
}
e.stopPropagation();
};
o2.EventHandler.stopPropagation(evt);
}
Preventing the Default Action
Each DOM node is associated with a default action for certain events. This is the “default behavior” of the element when no event handler is explicitly atached to it. For instance when you click a link, the browser reloads with the url of th link — that’s the default click event of that link. Or when you type into a textarea, the things you type actually appear inside the textarea — that’s the default keypress action of the textarea object. Or when you press enter in a form field, the form submits — that’s the default action of the form element.
Sometimes those default behaviors happen to be counter-productive to our application and we may need to suppress them. Here’s how:
/**
* @function {static} o2.EventHandler.preventDefault
*
* <p>Prevents the default action. When this method is called inside an even
* handling
* callback, the default action associated with that event is not triggered.
* Like,
* if it is an <code>onclick</code event on a link, then the browser does not go
* to
* the <code>href</code> of that link.</p>
*
* @param {Event} evt - the actual <code>DOM Event</code> object used internally
* in
* {@link o2.EventHandler.addEventListener}
*/
preventDefault: function(evt) {
o2.EventHandler.preventDefault = window.event ? function() {
window.event.returnValue = false;
return false;
} : function(e) {
if(!e) {
return;
}
if(e.preventDefault) {
e.preventDefault();
}
return false;
};
o2.EventHandler.preventDefault(evt);
},
These are the core methods, that we’ll almost always need.
And here are some extension methods:
Getting the Mouse Coordinates
/**
* @function {static} o2.EventHandler.getMouseCoordinates
*
* <p>Gets the current mouse coordinates.</p>
*
* @param {Event} evt - the actual <code>DOM Event</code> object used internally
* in {@link o2.EventHandler.addEventListener}
* @return the coordinates in the form of <code>{x: mouseX, y: mouseY}</code>
* where <code>x</code> is the distance from the top of the screen, and
* <code>y</code> is the distance from the left of the screen.
*/
me.getMouseCoordinates = function(evt) {
var e = getEventObject(evt);
if(!e) {
return {
x:0,
y:0
};
}
var posx = 0;
var posy = 0;
if (e.pageX) {
me.getMouseCoordinates = function(e) {
if(!e) {
return {
x:0,
y:0
};
}
posx = e.pageX || 0;
posy = e.pageY || 0;
return {
x:posx,
y:posy
};
};
return me.getMouseCoordinates(evt);
}
if (e.clientX) {
me.getMouseCoordinates = function(e) {
if(!e) {
return {
x:0,
y:0
};
}
var clientX = e.clientX || 0;
var clientY = e.clientY || 0;
posx = clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
posy = clientY + document.body.scrollTop +
document.documentElement.scrollTop;
return {
x:posx,
y:posy
};
};
return me.getMouseCoordinates(evt);
}
//the current event object neither has pageX, nor clientX defined.
return {
x:0,
y:0
};
};
Getting the Code of the Pressed Keyboard Key
/**
* @function {static} o2.EventHandler.getKeyCode
*
* <p>Gets the key code of the key-related event (keydown, keyup, keypress
* etc.).</p>
*
* @param {Event} evt - the actual <code>DOM Event</code> object used internally
* in {@link o2.EventHandler.addEventListener}
* @return the <code>keyCode</code> associated with the event as an
* <code>Integer</code>
*/
me.getKeyCode = function(evt) {
var e = getEventObject(evt);
if(!e) {
return null;
}
if(e.charCode) {
me.getKeyCode = function(e) {
return e.charCode;
};
return me.getKeyCode(evt);
}
if(e.keyCode) {
me.getKeyCode = function(e) {
return e.keyCode;
};
return me.getKeyCode(evt);
}
if(e.which) {
me.getKeyCode = function(e) {
return e.which;
};
return me.getKeyCode(evt);
}
return null;
};
Is it a Right Click?
/**
* @function {static} o2.EventHandler.isRightClick
*
* <p>Checks whether or not the curent action is a right click action.</p>
*
* @param {Event} evt - the actual <code>DOM Event</code> object used internally
* in {@link o2.EventHandler.addEventListener}
* @return <code>true</code> if the event is a right click event,
* <code>false</code> otherwise.
*/
me.isRightClick = function(evt) {
var e = getEventObject(evt);
/*
* According to W3C
* Left Button: 0
* Middle Button: 1
* Right Button: 2 (!)
*
* According to M$
* Lef Button: 1
* Middle Button: 4
* Right Button: 2 (!)
* Left and Right: 3
* Left and Middle: 5
* Right and Middle: 6
* All three: 7
* http://msdn.microsoft.com/en-us/library/ms533544(v=vs.85).aspx
*/
if(!e) {
return false;
}
if(e.which) {
me.isRightClick = function(e) {
return e.which == 3;
};
return me.isRightClick(evt);
}
if(e.button) {
me.isRightClick = function(e) {
return e.button == 2;
};
return me.isRightClick(evt);
}
return false;
};
Demo
Here’s a sample demo to test all these:
<html> <head> <title>o2.EventHandler Demo</title> </head> <body> <div> <a href="/" title="click me" id="ClickTest">click me</a> <a href="/" title="click me" id="RemoveClickTest">remove handler</a> </div> <div id="Output"> output </div> <div id="MouseCoordinates"> mouse coordinates. </div> <div id="KeyCodes"> key codes. </div> <script type="text/javascript" src="js/o2.js"></script> <script type="text/javascript" src="js/o2.domhelper.ready.js"></script> <script type="text/javascript" src="js/o2.eventhandler.core.js"></script> <script type="text/javascript" src="js/o2.eventhandler.extend.js"></script> <script type="text/javascript" src="js/o2.debugger.js"></script> <script type="text/javascript" src="js/eventhandler/main.js"></script> </body> </html>
And the main.js is:
/*global window, o2*/
o2.ready(function(){
var listen = o2.EventHandler.addEventListener;
var forget = o2.EventHandler.removeEventListener;
var prevent = o2.EventHandler.preventDefault;
var getCoords = o2.EventHandler.getMouseCoordinates;
var getCode = o2.EventHandler.getKeyCode;
var isRightClick = o2.EventHandler.isRightClick;
var clickTest = o2.$('ClickTest');
var removeClickTest = o2.$('RemoveClickTest');
var output = o2.$('Output');
var mouseCoordinates = o2.$('MouseCoordinates');
var keyCodes = o2.$('KeyCodes');
var listenClick = function(evt){
output.innerHTML = 'Hello';
prevent(evt);
};
listen(clickTest, 'click', listenClick);
listen(removeClickTest, 'click', function(evt){
prevent(evt);
output.innerHTML = 'Removed handler';
forget(clickTest, 'click', listenClick);
});
listen(document, 'mousemove', function(evt){
var coords = getCoords(evt);
mouseCoordinates.innerHTML = coords.x + ' ' + coords.y;
});
listen(document, 'keydown', function(evt){
keyCodes.innerHTML = getCode(evt);
});
listen(document, 'mousedown', function(evt){
prevent(evt);
if(isRightClick(evt)){
output.innerHTML += ' - is right click!';
}
});
});
…
That’s all for our o2.EventHandler object.
Now that our EventHandler is ready, we’re one step closer to creating a “mobile chat application“. Stay tuned for the next tutorials
And, as always, feel fee to add your comments and suggestions


Its really interesting to read from an expert in the field. You always learn something new
That’s my pleasure, Alexander.
I’m glad you’ve learned new stuff here
Thanks for the great work! Just a quick question though, why is the o2 event handler blocking the phonegap api?
Hi Vincent, and thank you.
We’ve been using o2.js with PhoneGap in GROU.PS mobile app in various mobile devices. I did not coincide with a blocking behavior.
I’m noting this down. I will look at it as soon as I have time.
Do you have some kind of demo code to test?
Cheers.
Great article. I noticed that you do string concatenation using:
var onEvent = ['on',evt].join(”);
Any reason for not doing it the most straightforward way?
var onEvent = ‘on’ + evt;
Thanks! I’m learning a lot from your blog!
Hi Andre,
I’m glad that the article was useful.
[].join is kind of a micro-optimization; and it’s slightly harder to read.
If you are joining a bunch of small strings, then + operator is good enough.
Array.prototype.join is faster to execute when you join a large number of strings (like when you are processing a template in a for loop)
It’s just a good habit (so that you won’t forget to use Array.join for larger Strings); yet it’s value in this particular case is debatable.
hope that helps,
Volkan