How to overwrite addEventListener in Firefox 3.x?

天涯浪子 提交于 2021-01-28 07:28:52

问题


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?


回答1:


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.




回答2:


addEventListener is inherited from the EventTarget prototype, not Element.

var placeToReplace;
if (window.EventTarget && EventTarget.prototype.addEventListener) {
  placeToReplace = EventTarget;
} else {
  placeToReplace = Element;
}

placeToReplace.prototype.oldaddEventListener = placeToReplace.prototype.addEventListener;
placeToReplace.prototype.addEventListener = function(event, handler, placeholder) {
  console.log("calling substitute");
  if (arguments.length < 3) {
    this.oldaddEventListener(event, handler, false);
  } else {
    this.oldaddEventListener(event, handler, placeholder);
  }
}
document.querySelector("div").addEventListener("click", function() {
  console.log("foo");
});
<div>Click me</div>


来源:https://stackoverflow.com/questions/57437767/how-to-overwrite-addeventlistener-in-firefox-3-x

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!