bigskysoftware / htmx

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

bootstrap dropdown menu fails after browser backward/forward with hx-push-url #1107

Closed BoPeng closed 1 year ago

BoPeng commented 1 year ago

It took me quite some time to debug a problem with bootstrap dropdown, and it turns out to be something related to htmx. Basiclly, I have a number of pages structured as

header with bootstrap dropdown menu
<div id="page-wrapper">
    <div id="page-content">
    page content
    </div>
</div>
footer with the bootstrap and htmx.js

Then on the menu, I navigate to another page with

href="#" hx-get="another_page" hx-target="#page-wrapper"
       hx-swap="innerHTML show:top" hx-push-url="true" hx-select="#page-content"

so the header is not updated when the new page is loaded.

Everything appears to work, but when I use the backward button to navigate to the original page, my dropdown menu stops working. More specifically, the click event is somehow triggered twice so the menu is toggle() on and then off.

I am not sure exactly what happens when the backward button is clicked, but is there a way to fix the dropdown menu issue, for example adjusting what JS script runs during the process?

PS: If someone wants to reproduce the problem, please

  1. visit https://bioworkflows.com, the top menu works
  2. click the chat icon to the right of the search bar
  3. click the back button of the browser, the top menu no longer works
BoPeng commented 1 year ago

Adding hx-history-elt to <div id="page-wrapper"> appears to be working so I am closing this ticket for now.

David-Guillot commented 1 year ago

@BoPeng it might be related to how you load the JS files. If you load them from within the <body>, using the htmx history cache, which replaces the body, will make your JS code be loaded/initialized twice. This would also explain why changing the element used by htmx to frame its cache works. But careful, this solution expects the given element to be present on all pages of your entire site. Maybe loading JS files from <head> with defer attribute would be a cleaner solution, but that depends on how they are compiled/bundled/etc.

BoPeng commented 1 year ago

Yes, it was puzzling why some pages work and some do not (some navigated with normal href and some use hx-get), but it is clear that the problem was caused by the re-executing of the JS files, like one copy from the HTML file, and one copy from the cached <body/>.

My entire site is derived from a base template with main content wrapped within <div id="page-wrapper"> so the hx-history-elt solution should work. It also has the advantage of not-caching <footer/> which is also in <body/>. However, this does remind me that it is better to put libraries such as bootstrap and htmx in the header and only leave content-processing JS code in the body. I am making the adjustment now.

BoPeng commented 1 year ago

However, given the popularity of bootstrap and the fact that bootstrap's official documentation puts bootstrap.js inside the body, I am afraid that many more people will be bitten by this exact problem. I would suggest that the htmx documentation give an explicit warning on including .js files inside body, or output a warning to console when it sees libraries such as bootstrap in the cache.