【问题标题】:How to overwrite addEventListener in Firefox 3.x?如何在 Firefox 3.x 中覆盖 addEventListener?
【发布时间】:2019-08-09 22:24:58
【问题描述】:

我正在制作一个包含各种垫片和 旧浏览器页面中的 polyfills。 问题是它们似乎都没有包含任何东西来制作 'addEventListener' 的第三个参数可选的,因为网页 通常省略它,它会给出“没有足够的论据”。到处都是错误。

我试图用这种方式覆盖它:

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);
        }

}

现在检查它是否替换了元素上的功能:

alert(document.querySelector("div").addEventListener)

提醒原文:

function addEventListener() {
    [native code]
}

检查 Element.prototype.addEventListener:

alert(Element.prototype.addEventListener)

显示被覆盖的版本。 所以我尝试覆盖这些:

HTMLElement.prototype.addEventListener,
EventTarget.prototype.addEventListener,
HTMLDivElement.prototype.addEventListener,
Object.prototype.addEventListener,
Node.prototype.addEventListener,

这些都不起作用。它们中的每一个都显示在何时被覆盖 警报,但都不会影响元素的功能。 我在 firebug DOM 检查器中搜索了它的任何实例 仍然显示原件,并覆盖它 'applicationCache'、'content'、'document'、'frames'、'parent'、'self' 和“顶部”。显然这也不起作用。

用户脚本在“文档开始”处运行,我检查它是否运行 在任何其他脚本之前,甚至在元素存在之前。 在页面加载后手动运行脚本也不会执行任何操作。 我发现覆盖元素上的函数的唯一方法是 直接选择该元素并在每个元素上覆盖它 一个一个的元素。

所以我的问题是:如何覆盖“addEventListener”函数 全局的每个元素,在 Firefox 3.x 中?

【问题讨论】:

  • Firefox 3 已有十多年的历史了。获得安全更新的希望已经过去了很长时间。你真的不应该使用它。
  • 我专门为旧浏览器上的 polyfill/shim 制作了一个用户脚本

标签: javascript firefox prototype addeventlistener


【解决方案1】:

他们在那里所做的很糟糕......幸运的是他们从那时起修复了它;-)

var el1 = document.createElement('div');
var el2 = document.createElement('div');

el1.addEventListener === el2.addEventListener; 

在 FF 3.2 中是 false...

这意味着他们为每个元素设置了自己的函数版本(可能是绑定版本)。

鉴于此,您唯一的方法是捕获所有元素并将您自己的版本一一附加到它们。

一种方法是覆盖document.createElement 方法,但这仅适用于由脚本创建的元素。 要捕捉标记中的那些,您可以使用 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>

但是,这不会捕获在 DOMContentLoaded 事件触发之前接收事件侦听器的标记中的那些。

对于这些,您可能还必须覆盖所有document.getElementByIddocument.querySelectorElement_instance.querySelectorNodeList getters、HTMLCollection getters 等。
这不是一项简单的任务,不幸的是我错过了为这个答案做这一切的动机,因为我在搜索错误时只使用 mozregression 的这个 FF 版本,但至少你明白了。

【讨论】:

  • 谢谢你,我只是认为会有一个原型或其他东西可以覆盖。那么它从哪里来的所有元素呢?在 Element.prototype 和 EventTarget.prototype 中覆盖后创建一个元素似乎仍然没有给他们新的
  • 确实,这正是这个答案所说的。共享同一原型链的两个元素将具有不同的 addEventListener 方法。我没有检查源代码,但它很可能在内部设置为我们无权访问的绑定函数。这就是为什么唯一的方法是手动覆盖每个实例的方法......
【解决方案2】:

addEventListener 继承自 EventTarget 原型,而不是 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");
});
&lt;div&gt;Click me&lt;/div&gt;

【讨论】:

  • 我也试过了,它在问题中列出。似乎仍然没有在页面中的元素上覆盖它
  • 它需要专门在不支持可选的第三个参数的旧浏览器上工作。 this 和 Element.prototype 适用于较新的浏览器,但当时可能不同
  • 我添加了条件代码来检查EventTarget 是否存在。我认为这应该可以作为适当的 polyfill。
  • Element 继承自 EventTarget,因此在 Element 的原型上设置 addEventListener 方法将使该版本可用于 Element 实例。 OP 所做的应该是可行的。
  • @Kaiido 确实,如果我用 PlaceToReplace = Element 替换我的条件,它可以在现代浏览器中工作。
猜你喜欢
  • 2011-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-06
  • 1970-01-01
  • 2019-11-04
  • 1970-01-01
相关资源
最近更新 更多