您可以不使用document.write销毁文档元素和事件侦听器吗? 附录:

问题描述

在以下示例中:

  • 如果右键单击该文档,它将告诉您正在监听。
  • 如果单击m1,它将替换文档元素,但是右键单击文档仍会通知您正在监听。您必须在顶部附近单击鼠标右键,因为该文档没有内容
  • 如果单击m2,它将覆盖文档内容,然后右键单击顶部附近的行将不再执行任何操作。在开发工具中检查文档可确认事件处理程序已消失。
  • 按下一个按钮后,您必须再次“运行代码段”才能尝试下一个,因为此演示具有破坏性。

使用此信息。有没有其他方法可以销毁文档并将其替换为新的文档,从而破坏事件处理程序,而无需使用document.write()函数

document.write()容易出错,用法"strongly discouraged",但我仍然希望能够销毁文档及其事件监听器。

document.addEventListener('contextmenu',function (e) {
  alert('still listening');
  e.preventDefault();
})

function m1() {
  var doc = document.implementation.createHTMLDocument();
  document.replaceChild(
    document.importNode(doc.documentElement,true),document.documentElement
  );
}
function m2() {
  document.close();
  document.write('<html></html>');
}
<button onclick="m1()">m1</button>
<button onclick="m2()">m2</button>

很明显,按钮/功能“ m1”未能达到我的目标,因为尽管替换了document元素,但出于某种原因保留了前一个document元素的事件处理程序。我想实现m2所能达到的目标,但不使用推荐使用的document.writedocument.close

附录:

这个问题严格是为了更好地理解语言的限制以及实现它们的引擎。请不要尝试在各行之间阅读或解决其他目标。这只是一个事情是否可能的问题。

我不需要知道如何删除事件侦听器或管理事件侦听器,或删除文档的所有子元素。我想知道是否有可能破坏document元素本身,不留任何<html>标签,也不留其事件监听器。 document.write()可以做到这一点,但我只是想知道是否有替代方法可以实现此确切目标,而没有其他假定目标。

解决方法

我只是意识到实际上可以,并且第一个示例m1实际上确实替换了documentElement ,并且删除了其事件监听器。有趣的是,一开始就没有事件侦听器,它们是在文档本身上,而不是在 document元素上。开发人员工具(在FireFox中)会欺骗您,并将其显示为已附加到<html>元素上,即使它们在技术上未附加到元素上也是如此。如果您修改示例以将事件实际附加到document.documentElement而不是文档本身,则在单击m1时将禁用该事件:

document.documentElement.addEventListener('contextmenu',function (e) {
  alert('still listening');
  e.preventDefault();
})

function m1() {
  var doc = document.implementation.createHTMLDocument();
  document.replaceChild(
    document.importNode(doc.documentElement,true),document.documentElement
  );
}
function m2() {
  document.close();
  document.write('<html></html>');
}
<button onclick="m1()">m1</button>
<button onclick="m2()">m2</button>

最初,我要说不,并且您不能这样做,正如用户Livingston Samuel在此similar but not identical question中指出的那样:“您不能用创建的Document对象替换当前文档对象或任何文档对象使用createHTMLDocument方法。” 坚持我最初的问题,这不是我要问的文档,而是documentElement。因此,鉴于上述修改后的代码,我会说是的,有可能。

有趣的是,从版本81开始,无论事件侦听器是附加到文档对象还是documentElement对象上,它们都显示在FireFox的html元素上。我可能希望它们根本不会显示它们附加到document时,或将它们放在某个抽象对象上。 iframe确实有一个可用的抽象对象,其标签为#document,但未用于此对象,也没有事件标签:

Document object in FireFox dev tools.

有趣的是document.write()实际上破坏了document对象上的事件,而不仅仅是document.documentElement。它可能记录在here中的某个位置,但我似乎找不到。

在测试Chrome之后,我注意到开发人员工具区分了附加到documentElement的事件:

documentElement contextmenu listener

和文档本身:

document contextmenu listener

,

我不确定这个问题的背后是什么,但我可能会猜想,当在页面中重新创建对象和事件时,您遇到了问题。

如果是这种情况,则可以在MDN(https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild)中看到,尽管您删除了HTML上的元素,但它们仍可能在内存中保留一段时间,等待垃圾收集器清理干净他们起来。

因此,如果您希望安全地删除包含事件的代码,最安全的方法是首先使用您喜欢的库(如jQuery)或通过本机JavaScript使用DOM来删除事件: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

一旦删除了所有事件,就可以删除所有元素,最安全的方法(从内存的角度来看)是通过循环,首先删除内部元素,然后删除父元素,依此类推。上。

,

您可以使用document.getElementById('id-here').innerHTML = "",如下所示:

<html id="the-website">

<body>
  <input type="button" onclick="document.getElementById('the-website').innerHTML = '';" value="Don't click me plz!">
</body>

</html>