b-fuze / deno-dom

Browser DOM & HTML parser in Deno
https://jsr.io/@b-fuze/deno-dom
MIT License
423 stars 47 forks source link

`parseFromString` pushes custom elements in the `<head>` to the `<body>` #180

Closed omar-azmi closed 4 days ago

omar-azmi commented 1 week ago

Hello, I noticed that parseFromString tends to push all elements in the <head> to the <body> as soon as it encounters either a custom element, or an element that is not intended for the head (such as <div> or <span>).

Example:

import { DOMParser } from "jsr:@b-fuze/deno-dom@0.1.48"

const my_html = `<html>
<head>
    <script id="0">console.log("Hello World")</script>
    <style id="1"></style>
    <custom-element id="2"></custom-element>
    <style id="3"></style>
    <div id="4"></div>
    <style id="5"></style>
</head>
<body></body>
</html>`

const doc = new DOMParser().parseFromString(my_html, "text/html")
console.log(doc.documentElement!.outerHTML)

This produces the (prettified) html:

<html>
<head>
    <script id="0">console.log("Hello World")</script>
    <style id="1"></style>
</head>
<body>
    <custom-element id="2"></custom-element>
    <style id="3"></style>
    <div id="4"></div>
    <style id="5"></style>
</body>
</html>

The expected output should have been:

<html>
<head>
    <script id="0">console.log("Hello World")</script>
    <style id="1"></style>
    <custom-element id="2"></custom-element>
    <style id="3"></style>
    <div id="4"></div>
    <style id="5"></style>
</head>
<body></body>
</html>

From what I've tested, this seems to be an issue with the parseFromString method, and not the outerHTML accessor, because if I replace an element inside of the head (e.g. element.replaceWith(doc.createElement("custom-element"))), then the custom element is not pushed into the body instead.

Is this by design? Because it certainly makes it extremely inconvenient to make transformations to head elements.

Thanks in advance!

omar-azmi commented 1 week ago

I've tried a few things in web browsers (chromium and firefox), and seems like DOMParser.prototype.parseFromString does in fact forcefully push custom elements in the head to the body.

In other words, the behavior of this library is consistent with browser implementations. I imagine that changing the behavior would not only be a non-goal, but also extremely difficult, given that it relies on servo's html5ever crate. So, please feel free to close this issue if that is the case.

b-fuze commented 4 days ago

Yeah since deno-dom relies on html5ever which is a standards-compliant HTML parser, so "preventing" this behaviour would be adding non-conforming logic.