htmx icon indicating copy to clipboard operation
htmx copied to clipboard

Swapping-in HTML adds nodes nested under noscript to the DOM

Open mqudsi opened this issue 3 years ago • 3 comments

There's a problem w/ the code (or specific approach) that htmx takes to get the browser to recognize HTML and scripts from targeted DOM elements swapped into an existing HTML document that leads to HTML elements nested within <noscript /> tags to be parsed and added to the DOM, when they wouldn't normally be.

This is probably best explained with an example. The following code sample points out three different issues that occur with this bug:

  1. noscript elements are found in the DOM,
  2. (additionally/therefore) css in a noscript context is evaluated and applied, and yet
  3. the noscript elements that were incorrectly added to the DOM are not visible despite being in the DOM (so there's a bug in the bug itself!).
<div class="payload">
    <p class="js-only">Hello, js browser.</p>
    <noscript>
        <p class="no-js">Hello, js-free client.</p>
        <style>
            .js-only {
                display: none !important;
            }
        </style>
    </noscript>
</div>

<button onclick="javascript:searchForNode()">Search for Node</button>
<button hx-get="page1.html" hx-target=".payload" hx-swap="outerHTML" hx-select=".payload">Swap w/ Self</button>

<script src="https://unpkg.com/[email protected]"></script>

<script>
    function searchForNode() {
        const node = document.querySelector("p.no-js");
        if (node) {
            alert("FAIL: A node from a no-js context was found in the DOM!");
        } else {
            alert("PASS: No no-js nodes were found in the DOM.");
        }
    }
</script>

Effectively, the htmx operation should be a no-op as the same document is retrieved and the outerHTML of the same node is replaced with itself. That is not the case.

Save this as page1.html and load it in a browser (from an http(s) context, not from a file:/// context, of course). Click the Search for Node button first and observe that the test passes. Click Swap w/ Self button, observe that the css style from within the <noscript /> tag is incorrectly applied to the document causing the .js-only element to become hidden, then click Search for Node again and observe that the test now fails (the p.no-js element should not be in the DOM but has been added to the DOM). Additionally observe that the DOM is in a weird transitional state where the contents of the <noscript /> tag have been parsed and added to the DOM, but at the same time, the p.no-js element isn't actually made visible, either.

mqudsi avatar Feb 16 '22 20:02 mqudsi

To feed the discussion: if you add another button to your page, that says:

<button onclick="javascript:document.querySelector('.payload').outerHTML = '<p class=\'js-only\'>Hello, js-supporting browser.</p><noscript><p class=\'no-js\'>Hello, non-js client.</p><style>.js-only {display: none !important;}</style></noscript>'">Swap with hard-coded stuff</button>

On click, Hello, js-supporting browser. is displayed properly, and <noscript> stuff stays unparsed.

So it seems you're right @mqudsi , htmx seems involved in this weird behavior.

David-Guillot avatar Apr 21 '22 13:04 David-Guillot

Hi, I test it with latest version of htmx and problem seems to be resolved.

mihalikv avatar Aug 25 '22 11:08 mihalikv