fix(web): event-delegate modal triggers so HTMX-swapped buttons work
The previous wiring attached click listeners on DOMContentLoaded, so any [data-modal-open] / [data-modal-close] / dialog.modal element that came in via a later HTMX partial swap silently lost its behaviour. The server-detail Actions partial reloads its reset/delete triggers on every state change, so reset was unclickable after the first state change post-load. Switch to a single delegated click handler on document. Same logic, but matches via Element.closest() so it works regardless of when an element was added to the DOM. No re-bind needed after HTMX swaps. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5eac51a93e
commit
87d56a0910
1 changed files with 31 additions and 24 deletions
|
|
@ -1,27 +1,34 @@
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
// Event delegation on document so partials swapped in via HTMX (or any
|
||||||
document.querySelectorAll("[data-modal-open]").forEach((trigger) => {
|
// later DOM mutation) still get modal behaviour without re-binding. The
|
||||||
trigger.addEventListener("click", (event) => {
|
// previous per-element wiring on DOMContentLoaded silently broke for
|
||||||
const targetId = trigger.getAttribute("data-modal-open");
|
// buttons that didn't exist at page load — e.g., the server-detail
|
||||||
const dialog = document.getElementById(targetId);
|
// Actions partial reloads its reset/delete triggers on every state
|
||||||
|
// change, and only the very first ones were ever wired up.
|
||||||
|
|
||||||
|
document.addEventListener("click", (event) => {
|
||||||
|
const opener = event.target.closest("[data-modal-open]");
|
||||||
|
if (opener) {
|
||||||
|
const dialog = document.getElementById(opener.getAttribute("data-modal-open"));
|
||||||
if (dialog && typeof dialog.showModal === "function") {
|
if (dialog && typeof dialog.showModal === "function") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
dialog.showModal();
|
dialog.showModal();
|
||||||
}
|
}
|
||||||
});
|
return;
|
||||||
});
|
}
|
||||||
|
|
||||||
document.querySelectorAll("dialog.modal").forEach((dialog) => {
|
const closer = event.target.closest("[data-modal-close]");
|
||||||
dialog.querySelectorAll("[data-modal-close]").forEach((closer) => {
|
if (closer) {
|
||||||
closer.addEventListener("click", (event) => {
|
const dialog = closer.closest("dialog.modal");
|
||||||
|
if (dialog) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog.addEventListener("click", (event) => {
|
|
||||||
if (event.target === dialog) {
|
|
||||||
dialog.close();
|
|
||||||
}
|
}
|
||||||
});
|
return;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Backdrop click: target IS the dialog (clicks on inner content
|
||||||
|
// don't bubble up as the dialog itself).
|
||||||
|
if (event.target.matches && event.target.matches("dialog.modal")) {
|
||||||
|
event.target.close();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue