I've made a simple example to illustrate the problem: https://jsfiddle.net/6gzb4cdj/4/
For reference, here are the original test files:
main.html:
<!DOCTYPE html>
<html hx-push-url="true">
<head>
<script src="https://unpkg.com/[email protected]/dist/htmx.min.js" defer="defer"></script>
</head>
<body>
<img src="https://htmx.org/img/bss_bars.png" width="100"/>
<div><a href="test.html" hx-get="test.html" hx-target="#main" hx-swap="outerHTML">Test</a></div>
<div id="main">
Main
</div>
</body>
</html>
test.html:
<div>
Test
</div>
Here's what happens:
The image is not part of the injected HTML, so does anyone know why it's being reloaded, or how to avoid it?
This even happens if you put the image element inside the #main <div>
Any help appreciated, as this means we're wasting lots of client and server resources, loading elements that has already been loaded.
I've turned off caching, and even tested using hx-swap="none", to discard the response, but this still happens.
This appears to be caused by hx-push-url="true" on the <html> element. When omitted, the reload of the image no longer happens. Unfortunately, moving that attribute to another element, including the link itself doesn't make the problem go away.
So this seems to be happening because HTMX deep-clones the body element as part of caching the page in its own history cache (using LocalStorage). It happens in this line: https://github.com/bigskysoftware/htmx/blob/2d3fbbf09f7e6792132dafa2f22b0feb5e194d6b/src/htmx.js#L3154 (elt.cloneNode(true)
).
Deep-cloning an element will apparently re-request every resource within it. At least this is visible in Firefox’s console as well as its network tab. I haven’t found such a log entry in Edge. So maybe it’s just a Firefox bug, I dunno. Personally I would have expected this to happen only when the node is actually inserted into the document, which HTMX doesn’t do.
The good news is, the browser should usually just take it from its cache and not actually request it from the server.
To prevent this issue, you can disable HTMX’s history cache by putting the attribute hx-history="false"
somewhere on the page. The history (i.e. browser back button) will still work as usual, but it’ll be handled entirely by the browser without any cloneNode(true)
or LocalStorage shenanigans.