bigskysoftware / htmx

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

No hx-get events on element loaded from a template after initial page load #652

Closed peterswords closed 2 years ago

peterswords commented 2 years ago

I am using HTMX with Alpine JS. Alpine allows you to conditionally load parts of a page based on "x-if" attributes attached to a template element. If such a template contains nested elements with hx-get attributes, the hx-get triggers only if it was shown when the page initially loaded.

This is easiest to show with an example. JsFiddle is here: https://jsfiddle.net/r5b8k90v/1/

Since the hx-get tries to load from a non-existent location /blah, you cannot see anything actually working (or failing to). However, you can use the Firefox or Chrome developer tools to see what events are attached. When you run the fiddle, you initially see one link (an anchor tag with a h-get and text 'Page 1').

Press the "Press Me" button to change a flag and make a second link visible (identical to the first except for differently named). While I can't show in this fiddle how the second link is broken, it can be seen in the browser developer tools that only the "Page1" link has an event attached, while the second link which was only revealed after initial page load is broken -- Picture 1, Picture 2.

Is there something that needs to be done to make HTMX elements "come alive" if they are incorporated into a page from a template after the initial page load?

peterswords commented 2 years ago

My apologies, it's covered in the documentation under '3rd Party Javascript'. You need to call htmx.process() on any content newly added to the DOM (also mentioned in Issue 571). Here is an updated JsFiddle: https://jsfiddle.net/p20tc6Lx/ (again, you'll need to use browser dev tools to see that a request is actually now issued for Page 2).

I've taken the Alpine approach of doing a $watch for a change of variable that causes the new content to be loaded. However, the $watch event fires before the content appears, so it's also necessary to use Alpine's $nextTick() to defer the htmx.process() until that happens.

1cg commented 2 years ago

Excellent. If you have time would you mind creating a docs pull request with this against the 3rd party section? I'm sure other people would be interested in Alpine and htmx working together properly.

dalito commented 2 years ago

@peterswords - You use $nextTick() to avoid a superfluous call of htmx.process() on intialisation? Sorry if this is obvious. - I am new to this. It also works without using $nextTick():

<div x-data="{p1: true, p2: false}"
     x-init="$watch('p2', value => htmx.process(document.querySelector('#p2')) )">
    <template x-if="p1">
        <div id="p1"><a hx-get="/page1" href="#">Page1</a></div>
    </template>
    <template x-if="p2">
        <div id="p2"><a hx-get="/page2" href="#">Page2</a></div>
    </template>
    <button @click = " p2=true ">Press Me</button>
</div>
peterswords commented 2 years ago

@dalito, weird, I could have sworn it wasn't working without the $nextTick, but you're right -- it seems fine now. It's also worth showing the more general case where the value might be toggled on and off: you don't want to do a htmx.process() if the content has been removed. I revised the JsFiddle here: https://jsfiddle.net/4d8fsq2y/

It also turns out that script defer on the Alpine JS library is vital. Without it the button's click event fires twice, and the value gets set and reset before we see the new content.

Thanks for the steer.

<div x-data="{show_new: false}"
     x-init="$watch('show_new', value => {
       if (show_new) {
         htmx.process(document.querySelector('#new_content'))
       }
    })">
  <button @click = "show_new = !show_new">Toggle New Content</button>
  <template x-if="show_new">
    <div id="new_content">
      <a hx-get="/server/newstuff" href="#">New Clickable</a>
    </div>
  </template>
</div>
peterswords commented 2 years ago

Excellent. If you have time would you mind creating a docs pull request with this against the 3rd party section? I'm sure other people would be interested in Alpine and htmx working together properly.

@1cg, old-school enterprise developer here. Forty years coding and I don't know how to do a pull request. However, even a tiny contribution to such a great project is worth a try, so I hope I've done it right.

1cg commented 2 years ago

Accepted! Thank you!