im making a greasemonkey userscript which puts all kinds of shims and polyfills in the page for old browsers. the problem is that none of them seems to include anything to make the third parameter of 'addEventListener' optional, and since webpages usually omit it, it gives a "Not enough arguments." error everywhere.
i tried to overwrite it this way:
Element.prototype.oldaddEventListener = Element.prototype.addEventListener;
Element.prototype.addEventListener = function(event,handler, placeholder) {
if (!arguments[2]) {
this.oldaddEventListener(event,handler, false);
}
else{
this.oldaddEventListener(event,handler, placeholder);
}
}
and now checking if it replaced the function on elements:
alert(document.querySelector("div").addEventListener)
alerts the original:
function addEventListener() {
[native code]
}
checking the Element.prototype.addEventListener:
alert(Element.prototype.addEventListener)
shows the overwritten version. so then i tried overwriting these:
HTMLElement.prototype.addEventListener,
EventTarget.prototype.addEventListener,
HTMLDivElement.prototype.addEventListener,
Object.prototype.addEventListener,
Node.prototype.addEventListener,
NONE of this worked. every of them shows to be overwritten when alerted, but none of it affects the function of elements. i searched in the firebug DOM inspector for any instances of it that were still showing the original, and overwritten it in 'applicationCache', 'content', 'document', 'frames', 'parent', 'self' and 'top'. obviously this didnt work either.
the userscript runs at 'document-start', and i checked that it runs before any other script and before the element even exists. running the script manually after the page loaded also does nothing. the only way i found to overwrite the function on elements is to directly select that element and overwrite it on each and every element one by one.
so my question is: how to overwrite the "addEventListener" function on every element globally, in Firefox 3.x?
That's quite bad what they did there... Luckily they fixed it since then ;-)
var el1 = document.createElement('div');
var el2 = document.createElement('div');
el1.addEventListener === el2.addEventListener;
is false
in FF 3.2...
This means that they were setting for every element its own version of the function (probably a bound version).
Given this, your only way is to catch all the elements and append to them your own version one by one.
One way is to overwrite the document.createElement
method, but this will work only on elements that are created by scripts.
To catch the ones that in the markup, you could use a TreeWalker.
function overwrite(target) {
if (target._addEvent_overwritten) return; // do it only once
// calling the Element.prototype.addEventListener throws an Error in FF3.x
var prevAddEventListener = target.addEventListener;
target.addEventListener = function(type, handler, options) {
prevAddEventListener.call(this, type, handler, !!options);
};
target._addEvent_overwritten = true;
}
// catch in document.createElement
// FF 3.x doesn't set the Document.prototype.createElement
var prevCreateElem = HTMLDocument.prototype.createElement;
HTMLDocument.prototype.createElement = function(type) {
var elem = prevCreateElem.call(this, type);
overwrite(elem);
return elem;
};
// not only Elements do have this method...
overwrite(window);
overwrite(document);
// catch after Document has been generated
document.addEventListener('DOMContentLoaded', function() {
// catch all Elements
var walker = document.createTreeWalker(
document.documentElement, NodeFilter.SHOW_ELEMENT, null, false
);
while (walker.nextNode())
overwrite(walker.currentNode);
});
// Tests
var elem = document.createElement('div');
elem.addEventListener('click', function(evt) {
this.textContent = 'clicked'
});
elem.textContent = 'click me (dynamic)';
document.body.appendChild(elem);
window.addEventListener('load', function() {
document.getElementById('in-doc').addEventListener('click', function() {
this.textContent = 'clicked';
});
});
<div id="in-doc">click me (in markup)</div>
However this won't catch the ones in markup that receive event listeners before the DOMContentLoaded event fired.
For these, you will probably have to also overwrite all of document.getElementById
, document.querySelector
, Element_instance.querySelector
, NodeList
getters, HTMLCollection
getters etc.
That's not a trivial task, and I unfortunately miss the incentives to do it all for this answer, since I only use this FF version from mozregression when searching about a bug, but at least you get the idea.