Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you destroy the document element and event listeners without using document.write()?

In the example below:

  • If you right click the document, it will tell you it's listening.
  • If you click m1 it will replace the document element, but right clicking the document will still inform you that it's listening. You must right click near the top because the document has no contents.
  • If you click m2 it will overwrite the document contents and right clicking near the top no-longer does anything. Examining the document in the development tools verifies that the event handlers are gone.
  • After pressing one button, you must "run code snippet" again to try the next because this demonstration is destructive.

With this information. Is there a different way to destroy the document and replace it with a new one, in such a way that the event handlers are destroyed, without using the document.write() function?

document.write() is prone to errors and its usage is "strongly discouraged", but I would still like to be able to destroy the document and it's event listeners.

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>

To be clear, button/function "m1" fails my goals, because although the document element was replaced, the event handlers from the previous document element were preserved for some reason. I would like to achieve what m2 achieves, but without using document.write and document.close which are recommended against.

Addendum:

This question is strictly for the sake of better understanding the limits of the language and the engines that implement them. Please do not try to read between the lines or solve some alternate goal. It's just a question of whether something is possible or not.

I don't need to know how to remove event listeners or manage event listeners, or remove all child elements of a document. I would like to know if it's possible to destroy the document element itself, leaving no <html> tag whatsoever and leaving none of its event listeners behind. This is possible with document.write() but I simply want to know if there are alternate means of achieving this exact goal, not any other assumed goals.

like image 357
ADJenks Avatar asked Nov 06 '22 05:11

ADJenks


1 Answers

I just realised that actually you can, and that the first example, m1, actually does replace the documentElement and removes its event listeners. The interesting thing is that there were no event listeners on it in the first place, they were on the document itself not the document element. The developer tools (in FireFox) tricks you and shows them as being attached to the <html> element, even though they aren't technically attached to an element. If you modify the example to actually attach the events to the document.documentElement instead of the document itself the event will be disabled when you click 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>

Originally I was going to say no, and that you can't do it, as user Livingston Samuel states in this similar but not identical question: "You cannot replace the current document object or any document object with the Document object created with createHTMLDocument method." Sticking to my original question though, it is not the document I was asking about, it was the documentElement. So given the modified code above, I will say that Yes, it is possible.

It is interesting to see that the event listeners are displayed on the html element in FireFox whether they are attached to the document object or the documentElement object, as of version 81. I might expect that they would instead not display them at all when attached to document, or put them on some abstract object. An iframe does have an abstract object available labeled #document, but it is not used for this and has no event label:

Document object in FireFox dev tools.

It is also interesting that document.write() actually destroys the events on the document object, not just the document.documentElement. It might be documented somewhere in here but I can't seem to find it.

After testing Chrome I noticed that the developer tools distinguishes between events being attached to the documentElement:

documentElement contextmenu listener

and the document itself:

document contextmenu listener

like image 159
ADJenks Avatar answered Nov 12 '22 10:11

ADJenks