bigskysoftware / htmx

</> htmx - high power tools for HTML
https://htmx.org
Other
35.35k stars 1.18k forks source link

OOB swap stripping containing element in some cases #1043

Open gone opened 1 year ago

gone commented 1 year ago

Have a repro at https://github.com/gone/oobtest/commit/8e6c2f31bb90f2b21dcc4759eb8f8eff764621cd

the html <li hx-swap-oob="beforeend:#messages"><div>message</message></li> once swapped in only results in <div>message</div> with the li removed

loveaphair commented 1 year ago

I noticed the same thing. the docs say that you can do something like

<table>
<tbody id="contacts-table">
    ...
  </tbody>
</table>

And in the POST response

<tr hx-swap-oob="beforeend:#contacts-table">
    <td>Joe Smith</td>
    <td>joe@smith.com</td>
</tr>

But what you end up getting is

<tbody id="contacts-table">
    <td>Jo Smith</td>
    <td>joe@smith.com</td>
</tbody>

with no<tr> tags at all. So any additional requests just add columns to the table on a single row. The correct (or at least currently functional) way to do it is to add the hx-swap-oob on the outer element. Like this:

<tbody hx-swap-oob="beforeend:#contacts-table">
    <tr>
        <td>Joe Smith</td>
        <td>joe@smith.com</td>
    </tr>
</tbody>
sean-reed commented 1 year ago

This seems to happen whenever the additional content is wrapped in a <tr> or <td> (maybe others too but these two definitely don't work, whereas additional content in a <div> or <p> do work).

For example:

<div id="outofbounds">
    Some out of bounds content.
</div>

<table>
    <tr>
        <td>
            <div>
                A table row to swap out.
            </div>
        </td>
        <td>
            <button hx-get="url" hx-target="closest tr">Swap Row</button>   
        </td>
    </tr>
</table>

and responding to the htmx get with:

<tr>
    <td>
        <div>
            Table row was swapped out.
        </div>
    </td>
    <td>
        <button hx-get="url" hx-target="closest tr">Swap Row</button>   
    </td>
</tr>
<div id="outofbounds" hx-swap-oob="true">Out of bounds content swapped</div>

does not work as the tags are getting stripped. The outofbounds swap on its own in the response works and the table row swap on its own also works, but not together.

infogulch commented 1 year ago

Have you experimented with the htmx.config.useTemplateFragments configuration setting?

HTMX works by taking the text http response, turning it into a brand new document, then inspecting it to pull nodes out of the new DOM to place them into the actual DOM of the page. But this strategy doesn't work very well for tables, since tables have a bunch of special rules around when table-related node types are allowed and valid. The useTemplateFragments changes the strategy to wrap the http response in <template></template> tags, which has looser rules about table-related nodes.

I believe some people having trouble with table nodes found this setting to be helpful.

See:

sean-reed commented 1 year ago

Hi @infogulch

I've just tried this and it didn't fix the problem unfortunately. Inserting a row in a table is working fine for me, it's just when out of bounds content is included in the response that the problem occurs. I'd guess that the htmx code that is pulling out the out of bounds part of the response is mangling the other content wrapped in <tr> tags.

RoToRa commented 1 year ago

I've just tried this and it didn't fix the problem unfortunately. Inserting a row in a table is working fine for me, it's just when out of bounds content is included in the response that the problem occurs. I'd guess that the htmx code that is pulling out the out of bounds part of the response is mangling the other content wrapped in <tr> tags.

I just encountered the same problem. I have a server response containing the regular content (in my case a tbody) plus an out-of-bounds element (a section). As a workaround I solved it by wrapping an additional <table> around the <tbody> and added hx-select="tbody" to the triggering element.

RoToRa commented 1 year ago

Ok, I got around to do some debugging. It seems that even when using template fragments the DOMParser can't handle some combinations of elements that normally can't be siblings, which can happen when using oob swaps. I only tested with Firefox, but I wouldn't be surprised if it is different from browser to browser.

hx-select (and possibly hx-select-oob - I haven't checked that out properly yet) can be used as workaround in some cases, but in cases where HX-Retarget is used this would require implementation of HX-Reselect (and maybe HX-Reselect-oob).

Another thing that could be a solution, could be to supported hx-oob-swap on non-top-level elements.

However in my opinion, a better solution could be to implement a separator between the elements, for example in form of an HTML comment such as <!--oob-->, which would allow splitting the fragments before passing them separately to the DOMParser.

infogulch commented 3 weeks ago

This may be resolved with the release of 2.0 which includes a new config option: set the htmx.config.allowNestedOobSwaps config option to false, and template fragments with the hx-swap-oob attribute will no longer be swallowed.

See also the docs: https://htmx.org/attributes/hx-swap-oob/#nested-oob-swaps

The issue and PR that raises and implements the feature: #1133 #1235