Is it possible to extend the EventTarget interface?
I'm looking for an answer if this is possible, or even a good idea.
Usually, as a fix for IE8 and earlier versions, a workaround similar to this is used:
var addEvent = function(obj, e, cb) {
if(obj.attachEvent) {
return obj.attachEvent('on' + e, cb);
} else {
obj.addEventListener(e, cb, false);
return true;
}
}
and then the programmer would always use addEvent
instead of addEventListener
.
Is there any way or point to create an addEventListener
function that would wrap around the attachEvent
when it is undefined?
I imagine it's a bit tricky since the EventTarget can be a DOM element, the document, or the window, or somethign else.
It depends on how far back you want to go. On IE8, you can extend Element.prototype
to add features to all HTML elements, which is 90% at least of the work. I'm fairly sure you can't do that in IE6 (PrototypeJS had to resort to extending individual Element
instances), I don't remember about IE7. Unless you're targeting east-Asian markets, you can basically ignore IE7 and earlier, though.
Here's an example of how you do that:
(function() {
// Functions for IE
function stopPropagation() {
this.cancelBubble = true;
}
function preventDefault() {
this.returnValue = false;
}
function addEventUsingAttach(eventName, handler)
{
this.attachEvent("on" + eventName, function() {
var e = window.event;
e.stopPropagation = stopPropagation;
e.preventDefault = preventDefault;
handler.call(this, e);
});
}
// Function to add `addEvent` to the given target object
function extendIt(target)
{
if (target.addEventListener) {
target.addEvent = Element.prototype.addEventListener;
}
else {
target.addEvent = addEventUsingAttach;
}
}
// Add it to `Element.prototype` if we have it
if (typeof Element !== "undefined" &&
typeof Element.prototype !== "undefined") {
extendIt(Element.prototype);
}
// Add it to `window` and `document` (etc.)
extendIt(window);
extendIt(document);
})();
Live Example | Source
Then you manually extend the other EventTargets, like window
and document
(see the end of the code listing above).
Original answer: I originally misread your question to be about adding addEventListener
, specifically, on IE8, whereas in fact your question is quite clearly not about adding that, but adding your own addEvent
. I'm leaving this answer in place for other readers:
It depends on how far back you want to go. On IE8, you can extend Element.prototype
to add addEventListener
to it, and that will be used by all HTML elements on the page (see below). The shim you add can't support the capturing phase, though, because IE didn't support it until they supported addEventListener
natively. I'm fairly sure you can't extend Element.prototype
on earlier versions (IE7, IE6), PrototypeJS had to fall back to extending specific elements for older versions of IE. But it works in IE8.
Having extended Element.prototype
, then you'd have to manually extend the other event targets you mentioned, but extending Element.prototype
does most of the work.
But if you do this and you include third-party scripts, beware that they may assume a correct implementation of addEventListeneer
complete with the capturing phase.
Here's a basic shim for adding addEventListener
to IE8:
(function() {
function stopPropagation() {
this.cancelBubble = true;
}
function preventDefault() {
this.returnValue = false;
}
if (typeof Element !== "undefined" &&
typeof Element.prototype !== "undefined" &&
!Element.prototype.addEventListener) {
Element.prototype.addEventListener = function(eventName, handler, useCapture) {
if (useCapture) {
throw "Browser doesn't support capturing phase";
}
this.attachEvent("on" + eventName, function() {
var e = window.event;
e.stopPropagation = stopPropagation;
e.preventDefault = preventDefault;
handler.call(this, e);
});
};
}
})();
Live Example | Source