Closed hirasso closed 2 years ago
As a hacky workaround, I can do this to clean-up the component before htmx takes it's snapshot:
<ul x-data="{ colors: ['Red', 'Orange', 'Yellow'], innerHTML: null, init: () => innerHTML = $el.innerHTML, destroy: () => $el.innerHTML = innerHTML }"
x-on:htmx:before-history-save.window.camel="destroy()">
<template x-for="color in colors">
<li x-text="color"></li>
</template>
</ul>
This saves the innerHTML
of the component before it renders the first time and resets it before HTMX takes it's snapshot. But probably there is a cleaner solution?
What you have is, unfortunately, the recommended solution.
In htmx 2.0, I'm going to think harder about how to deal with HTML that's been mutated by javascript and the history cache. It's a hard problem.
Yes, it's a hard problem, indeed. I can actually imagine that it won't be possible to cleanly solve it for every last use case. But some official documentation on how to deal with DOM-altering third-party JS would already be very helpful, IMHO.
Here's my take on @hirasso's workaround (thanks!). It stores pristine snapshots of Alpine components' HTML in data attributes (I'm not proud of it but look how easy it is! :smile:) and restores them after HTMX has restored its snapshot of the whole DOM.
window.saveHtmxHistorySnapshot = (el) => {
el.dataset.htmxHistorySnapshot = el.outerHTML;
};
addEventListener('htmx:historyRestore', () => {
const els = document.querySelectorAll("[data-htmx-history-snapshot]");
for (const el of els) {
const snapshot = document.createElement("template");
snapshot.innerHTML = el.dataset.htmxHistorySnapshot;
const snapshotEl = snapshot.content.firstChild;
el.replaceWith(snapshotEl);
htmx.process(snapshotEl);
}
});
Usage:
<div x-data x-init="saveHtmxHistorySnapshot($el)">
...
</div>
Note that this hack is not specific to Alpine.js, and I use it to work around the same kind of issues with highlight.js.
Do these workarounds actually work? I've tried both suggestions with a Custom Element i'm building and what i'm observing is that the hx- attributes stop working if you press back sometimes. I'll try to create a minimal repro if i have time.
It also seems like the saved localStorage entry still contains the mutated HTML instead of the reset HTML despite having reset innerHTML on beforeHistorySave.
Here's the version of the Alpine.js solution by @hirasso that I use:
<ul x-data="{ colors: ['Red', 'Orange', 'Yellow'] }"
x-init="$data.innerHTML = $el.innerHTML"
x-on:htmx:before-history-save.window="$el.innerHTML = $data.innerHTML">
<template x-for="color in colors">
<li x-text="color"></li>
</template>
</ul>
Note 1: .camel
on the event attribute isn't necessary since htmx also fires a kebab case version of the event
Note 2: when I tried to use his version, it didn't work since it was missing using this.
for access to properties in x-data
Note 3: $data.
in the event attribute isn't actually necessary, since it's initialized at that point, but... symmetry
Different version:
<ul x-data="{ colors: ['Red', 'Orange', 'Yellow'] }"
x-on:htmx:before-history-save.window="htmx.findAll($el, ':scope > :not(template)').forEach(node => htmx.remove(node))">
<template x-for="color in colors">
<li x-text="color"></li>
</template>
</ul>
Or: x-on:htmx:before-history-save.window="$el.replaceChildren($el.firstElementChild)"
Here's a fixed version of the original:
<ul x-data="{
colors: ['Red', 'Orange', 'Yellow'],
init: () => this.innerHTML = $el.innerHTML,
reset: () => $el.innerHTML = this.innerHTML
}" x-on:htmx:before-history-save.window="reset()">
<template x-for="color in colors">
<li x-text="color"></li>
</template>
</ul>
Hi there! I have a site with
hx-boost
enabled. I also want to use Alpinejs for a few things. This, for example, would render a list with the three colors in it (see documetation):It works just as expected if I first visit the page that contains this snippet. But when I navigate away and then back to the page using the browser's back-button, this happens: The previously rendered list is already there, but is again evaluated, leading to errors. Basically what is happening is, HTMX saves the state of the DOM after it was altered by other scripts, which leads to side-effects like this.
Are there any known approaches to solving these kind of problems?