capricorn86 / happy-dom

A JavaScript implementation of a web browser without its graphical user interface
MIT License
3.28k stars 201 forks source link

`document.write` is not working as browsers #1376

Open aralroca opened 6 months ago

aralroca commented 6 months ago

After doing this:

const doc = document.implementation.createHTMLDocument();
doc.open();
doc.write('<html>')
doc.write('<head>')
doc.write('<title>Title</title>')
doc.write('</head>')
doc.write('<body>')
doc.write('<span>Body</span>')
doc.write('</body>')
doc.write('</html>')
doc.close();

console.log(doc.documentElement.outerHTML)

I expect:

<html>
  <head>
    <title>Title</title>
  </head>
  <body>
    <span>Body</span>
  </body>
</html>

(Same behavior in Chrome, Safari and Firefox)

But I'm getting this error:

DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.

I see that is related with this code:

https://github.com/aralroca/happy-dom/blob/d7c7d45b9190663ca545aa6184a28eb812ab46ca/packages/happy-dom/src/nodes/document/Document.ts#L897-L904

Here is creating the body twice, and then in the head is using the body that is not appended. However, changing this:

-documentElement.appendChild(this.createElement('body'));
+documentElement.appendChild(body);

Resolves that it does not crash, but the behavior is different and I don't know how to solve it (please, some help). Now the HTML generated is this one:

<html>
  <head>
  </head>
  <body>
    <head>
    </head>
    <title>Title</title>
    <span>Body</span>
  </body>
</html>

I think this is because of the doc.open() not behaving properly. So we can reproduce the same HTML in browsers removing the doc.open():

const doc = document.implementation.createHTMLDocument();
-doc.open();
doc.write('<html>')
doc.write('<head>')
doc.write('<title>Title</title>')
doc.write('</head>')
doc.write('<body>')
doc.write('<span>Body</span>')
doc.write('</body>')
doc.write('</html>')
-doc.close();

console.log(doc.documentElement.outerHTML)

But this is not what we want. This is necessary to process HTML in streaming in a separate document and to be able to parse the nodes as they come in.

[!NOTE]

Same error happens with document.write

Any idea how to solve it? Thanks