diff --git a/packages/happy-dom/src/html-parser/HTMLParser.ts b/packages/happy-dom/src/html-parser/HTMLParser.ts
index e664d399..6dc2ed98 100755
--- a/packages/happy-dom/src/html-parser/HTMLParser.ts
+++ b/packages/happy-dom/src/html-parser/HTMLParser.ts
@@ -186,6 +186,31 @@ export default class HTMLParser {
};
}
+ if (this.rootNode instanceof this.window.HTMLHtmlElement) {
+ const head = this.rootDocument.createElement('head');
+ const body = this.rootDocument.createElement('body');
+ while (this.rootNode[PropertySymbol.nodeArray].length > 0) {
+ this.rootNode[PropertySymbol.removeChild](
+ this.rootNode[PropertySymbol.nodeArray][
+ this.rootNode[PropertySymbol.nodeArray].length - 1
+ ]
+ );
+ }
+
+ this.rootNode[PropertySymbol.appendChild](head);
+ this.rootNode[PropertySymbol.appendChild](body);
+
+ this.documentStructure = {
+ nodes: {
+ doctype: null,
+ documentElement: this.rootNode,
+ head,
+ body
+ },
+ level: HTMLDocumentStructureLevelEnum.documentElement
+ };
+ }
+
let match: RegExpExecArray;
let lastIndex = 0;
diff --git a/packages/happy-dom/src/window/BrowserWindow.ts b/packages/happy-dom/src/window/BrowserWindow.ts
index 85e862ce..a345c776 100644
--- a/packages/happy-dom/src/window/BrowserWindow.ts
+++ b/packages/happy-dom/src/window/BrowserWindow.ts
@@ -1701,14 +1701,14 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
this[PropertySymbol.mutationObservers] = [];
// Disconnects nodes from the document, so that they can be garbage collected.
- const childNodes = this.document.body[PropertySymbol.nodeArray];
+ const childNodes = this.document[PropertySymbol.nodeArray];
while (childNodes.length > 0) {
// Makes sure that something won't be triggered by the disconnect.
if ((childNodes[0]).disconnectedCallback) {
delete (childNodes[0]).disconnectedCallback;
}
- this.document.body.removeChild(childNodes[0]);
+ this.document[PropertySymbol.removeChild](childNodes[0]);
}
// Create some empty elements for scripts that are still running.
@@ -1717,7 +1717,7 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
const bodyElement = this.document.createElement('body');
htmlElement.appendChild(headElement);
htmlElement.appendChild(bodyElement);
- this.document.body.appendChild(htmlElement);
+ this.document[PropertySymbol.appendChild](htmlElement);
if (this.location[PropertySymbol.destroy]) {
this.location[PropertySymbol.destroy]();
diff --git a/packages/happy-dom/test/browser/BrowserPage.test.ts b/packages/happy-dom/test/browser/BrowserPage.test.ts
index 12528155..cc0b5139 100644
--- a/packages/happy-dom/test/browser/BrowserPage.test.ts
+++ b/packages/happy-dom/test/browser/BrowserPage.test.ts
@@ -127,6 +127,9 @@ describe('BrowserPage', () => {
const frame1 = BrowserFrameFactory.createChildFrame(page.mainFrame);
const frame2 = BrowserFrameFactory.createChildFrame(page.mainFrame);
+ // Should work even if the body is removed.
+ frame2.document.body.remove();
+
await page.close();
expect(browser.defaultContext.pages.length).toBe(0);
diff --git a/packages/happy-dom/test/html-parser/HTMLParser.test.ts b/packages/happy-dom/test/html-parser/HTMLParser.test.ts
index 09c5963b..3c9806d8 100644
--- a/packages/happy-dom/test/html-parser/HTMLParser.test.ts
+++ b/packages/happy-dom/test/html-parser/HTMLParser.test.ts
@@ -2104,5 +2104,23 @@ describe('HTMLParser', () => {