3

If I use hx-swap-oob, then I get an error:

<script src="https://unpkg.com/htmx.org@1.3.3/dist/htmx.js"></script>
<div id="sum">?</div>
<table>
 <tr>
  <td><button 
   hx-get="https://run.mocky.io/v3/b5a6902e-fc46-479b-92e4-9b4befc7a920"
   hx-target="closest tr">1</button>
  </td>
  <td>one</td>
 </tr>
</table>

After pressing "Run Code Snippet" and then press 1:

htmx:swapError
{
  "message": "e.querySelectorAll is not a function",
  "filename": undefined,
  "lineno": undefined,
  "colno": undefined
}

The mocky http endpoint returns this:


<tr>
  <td>2</td><td>two</td>
 </tr>

<div id="sum" hx-swap-oob="true">MAGIC</div>

In above example I use the non minified version, so the error message is: eltOrSelector.querySelectorAll is not a function

If I use this endpoint, it does not fail: https://run.mocky.io/v3/2ab904eb-23a9-4006-b68b-f112b55841f3

But in my usecase the new html fragment should be <tr>...</tr>, not a <div>.....

JS stacktrace:

Uncaught TypeError: eltOrSelector.querySelectorAll is not a function
    at findAll (htmx.js:295)
    at handleOutOfBandSwaps (htmx.js:501)
    at selectAndSwap (htmx.js:712)
    at doSwap (htmx.js:2284)
    at handleAjaxResponse (htmx.js:2358)
    at XMLHttpRequest.xhr.onload (htmx.js:2163)

Update

I could narrow down the issue to this:

devtools

This fails:

makeFragment('<tr><td>a</td></tr> <div>X</div>')

Update2

hmmm, now I know why it fails:

makeFragment

parseFromString() of Chrome is the root-cause:

parseFromString

Update3

Follow-up question: Make parseFromString() parse without validation

Update4

I created an issue, hoping someone with more creativity has an idea how to solve this: https://github.com/bigskysoftware/htmx/issues/469

guettli
  • 25,042
  • 81
  • 346
  • 663
  • 1
    I only discover htmx with this question, so I am really not sure about it, but for your response to have the tr>td part parsed correctly, it would need at least a `` element around. And indeed, using [this end-point](https://run.mocky.io/v3/39b3dd91-9129-4308-a76e-4b7fc82b7e44) it writes "MAGIC".
    – Kaiido Apr 28 '21 at 09:49
  • @Kaiido yes, you are right the result looks good with your endpoint. But if you inspect the HTML with devtools, then there is the from the response inside the . That's not valid. But you idea brought me an idea which I will try next ...
    – guettli Apr 28 '21 at 10:52

2 Answers2

1

As per your issue, HTMX team has created a config that can be enabled to support table fragments like this. Documented example here: https://htmx.org/examples/update-other-content/#oob. As noted, it is not IE11 compatible.

htmx.config.useTemplateFragments = true;

Execute the following in a js file after htmx has been loaded.

Dzone64
  • 98
  • 7
0

I created a new endpoint which returns a slightly different HTML fragment:

<table>
 <tr id="row1">
  <td>2</td><td>two</td>
 </tr>
</table>

<div id="sum" hx-swap-oob="true">MAGIC</div>

<script src="https://unpkg.com/htmx.org@1.3.3/dist/htmx.js"></script>
<div id="sum">?</div>
before table
<table>
 <tr id="row1">
  <td><button 
   hx-get="https://run.mocky.io/v3/28a8e653-491c-4186-abd4-4f48f3579273"
   hx-target="#row1" hx-select="#row1">1</button>
  </td>
  <td>one</td>
 </tr>
</table>
after table

HTMX uses parseFromString() internally, and this method validates the input. If there is non valid HTML, it might truncate elements.

makeFragment() of HTMX wraps the string which it sends to parseFromString(). HTMX looks at the first element in the response. In above question it is <tr>. Thus htmx uses <table> to wrap the whole http response.

makeFragment()

The solution is the return a "normal" element to htmx (not <tr>).

Normal html fragments in htmx are exactly one element, so that it does not matter. If you use hx-swap-oob the server sends several elements to the client.

This is more or less a work-around. It would be great if htmx would handle the original string.

guettli
  • 25,042
  • 81
  • 346
  • 663