fix(lightbox): keep arrow clicks from leaking to backdrop close, raise controls above image
Logs from the user's last session showed that after walking through all images and trying to go back from the last one, a backdrop click fired (target===overlay) and closed the modal — even though the user believed they clicked the prev arrow. Two reinforcing causes: 1. The image (.lightbox-image) is a sibling AFTER the buttons in the DOM with no z-index, so paint order put the image on top of the absolute-positioned arrows. Where the image's max-width/height box overlapped the arrows, clicks landed on the image instead of the arrow, and clicks in the gap between image and arrow hit the overlay backdrop. 2. Even when an arrow handler did fire, the click bubbled up to the overlay's click handler. While target===overlay was false in that path, the next click sometimes did land on the backdrop, and the close button had the same exposure. Fix: - Stack the controls above the image: image gets z-index:1, every .lightbox button gets z-index:2. - stopPropagation on prev/next/close button clicks AND on the image click — guarantees they can never bubble into the overlay's backdrop-close handler. Backdrop close still works on actual backdrop clicks. - Bump button background to rgba(0,0,0,.55) (was .08 white on dark) so the arrows are clearly visible against the image. Also strip the [lazyflat.lightbox] DEBUG(lightbox) tracer logs and the window.error catch-all — original symptom is fixed and the existing flow is confirmed working in user's logs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c379bc989f
commit
e7f5cb9bee
2 changed files with 18 additions and 82 deletions
|
|
@ -142,12 +142,13 @@ body:has(#v_map:checked) .view-map { display: block; }
|
|||
animation: lightbox-fade .15s ease-out; }
|
||||
.lightbox-image { max-width: 92vw; max-height: 88vh; object-fit: contain;
|
||||
border-radius: 8px; box-shadow: 0 10px 40px rgba(0,0,0,.5);
|
||||
background: #0c1726; }
|
||||
.lightbox button { background: rgba(255,255,255,.08); color: #fff; border: 0;
|
||||
background: #0c1726; position: relative; z-index: 1; }
|
||||
.lightbox button { background: rgba(0, 0, 0, .55); color: #fff; border: 0;
|
||||
border-radius: 9999px; cursor: pointer;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
transition: background .15s, transform .05s; padding: 0; }
|
||||
.lightbox button:hover { background: rgba(255,255,255,.22); }
|
||||
transition: background .15s, transform .05s; padding: 0;
|
||||
z-index: 2; }
|
||||
.lightbox button:hover { background: rgba(0, 0, 0, .8); }
|
||||
.lightbox button:active { transform: scale(.96); }
|
||||
.lightbox-close { position: absolute; top: 1.25rem; right: 1.25rem;
|
||||
width: 2.5rem; height: 2.5rem; }
|
||||
|
|
|
|||
|
|
@ -90,11 +90,6 @@ document.addEventListener("click", (ev) => {
|
|||
.then((html) => {
|
||||
pane.innerHTML = html;
|
||||
pane.dataset.loaded = "1";
|
||||
// DEBUG(lightbox): confirm fetched partial actually contains <button class="flat-gallery-tile">
|
||||
const tileCount = pane.querySelectorAll(".flat-gallery-tile").length;
|
||||
const aTagCount = pane.querySelectorAll("a.flat-gallery-tile").length;
|
||||
console.log("[lazyflat.lightbox] partial loaded flat=%s tiles=%d (a-tags=%d, html-snippet=%o)",
|
||||
flatId, tileCount, aTagCount, html.slice(0, 200));
|
||||
})
|
||||
.catch(() => {
|
||||
pane.innerHTML = '<div class="px-4 py-5 text-sm text-slate-500">Detail konnte nicht geladen werden.</div>';
|
||||
|
|
@ -141,22 +136,13 @@ document.addEventListener("DOMContentLoaded", openDeepLinkedFlat);
|
|||
// at runtime, which conflicted with our class toggle and kept the modal
|
||||
// invisible after open().
|
||||
(function () {
|
||||
// DEBUG(lightbox): start-up trace, remove once the viewer is confirmed working.
|
||||
console.log("[lazyflat.lightbox] IIFE start");
|
||||
const overlay = document.getElementById("lazyflat-lightbox");
|
||||
console.log("[lazyflat.lightbox] overlay element:", overlay);
|
||||
if (!overlay) {
|
||||
console.warn("[lazyflat.lightbox] #lazyflat-lightbox not in DOM; viewer disabled");
|
||||
return;
|
||||
}
|
||||
if (!overlay) return;
|
||||
const imgEl = overlay.querySelector(".lightbox-image");
|
||||
const counterEl = overlay.querySelector(".lightbox-counter");
|
||||
const prevBtn = overlay.querySelector("[data-lightbox-prev]");
|
||||
const nextBtn = overlay.querySelector("[data-lightbox-next]");
|
||||
const closeBtn = overlay.querySelector("[data-lightbox-close]");
|
||||
console.log("[lazyflat.lightbox] children:",
|
||||
{ imgEl: !!imgEl, counterEl: !!counterEl, prevBtn: !!prevBtn,
|
||||
nextBtn: !!nextBtn, closeBtn: !!closeBtn });
|
||||
let urls = [];
|
||||
let idx = 0;
|
||||
|
||||
|
|
@ -169,34 +155,13 @@ document.addEventListener("DOMContentLoaded", openDeepLinkedFlat);
|
|||
}
|
||||
|
||||
function open(list, startIdx) {
|
||||
console.log("[lazyflat.lightbox] open() called", { count: list && list.length, startIdx, list });
|
||||
if (!list.length) {
|
||||
console.warn("[lazyflat.lightbox] open() bailed — empty url list");
|
||||
return;
|
||||
}
|
||||
if (!list.length) return;
|
||||
urls = list;
|
||||
idx = Math.max(0, Math.min(startIdx | 0, urls.length - 1));
|
||||
overlay.style.display = "flex";
|
||||
overlay.setAttribute("aria-hidden", "false");
|
||||
document.body.classList.add("lightbox-open");
|
||||
render();
|
||||
// DEBUG(lightbox): dump everything we'd want to know if clicks pass through.
|
||||
const cs = getComputedStyle(overlay);
|
||||
const rect = overlay.getBoundingClientRect();
|
||||
console.log("[lazyflat.lightbox] open() done", {
|
||||
display: cs.display, position: cs.position, zIndex: cs.zIndex,
|
||||
top: cs.top, left: cs.left, right: cs.right, bottom: cs.bottom,
|
||||
pointerEvents: cs.pointerEvents,
|
||||
rect: { x: rect.x, y: rect.y, w: rect.width, h: rect.height },
|
||||
viewport: { w: window.innerWidth, h: window.innerHeight, scrollY: window.scrollY },
|
||||
src: imgEl.src,
|
||||
});
|
||||
const prevRect = prevBtn.getBoundingClientRect();
|
||||
const nextRect = nextBtn.getBoundingClientRect();
|
||||
console.log("[lazyflat.lightbox] arrow rects", {
|
||||
prev: { hidden: prevBtn.hidden, x: prevRect.x, y: prevRect.y, w: prevRect.width, h: prevRect.height },
|
||||
next: { hidden: nextBtn.hidden, x: nextRect.x, y: nextRect.y, w: nextRect.width, h: nextRect.height },
|
||||
});
|
||||
}
|
||||
|
||||
function close() {
|
||||
|
|
@ -208,31 +173,21 @@ document.addEventListener("DOMContentLoaded", openDeepLinkedFlat);
|
|||
}
|
||||
|
||||
function step(delta) {
|
||||
console.log("[lazyflat.lightbox] step()", { delta, from: idx, max: urls.length - 1 });
|
||||
const next = idx + delta;
|
||||
if (next < 0 || next >= urls.length) {
|
||||
console.log("[lazyflat.lightbox] step() out of range — ignored");
|
||||
return;
|
||||
}
|
||||
if (next < 0 || next >= urls.length) return;
|
||||
idx = next;
|
||||
render();
|
||||
}
|
||||
|
||||
prevBtn.addEventListener("click", (ev) => {
|
||||
console.log("[lazyflat.lightbox] prevBtn click handler fired", { target: ev.target });
|
||||
step(-1);
|
||||
});
|
||||
nextBtn.addEventListener("click", (ev) => {
|
||||
console.log("[lazyflat.lightbox] nextBtn click handler fired", { target: ev.target });
|
||||
step(1);
|
||||
});
|
||||
closeBtn.addEventListener("click", (ev) => {
|
||||
console.log("[lazyflat.lightbox] closeBtn click handler fired");
|
||||
close();
|
||||
});
|
||||
// stopPropagation on every control so a click on a button can never bubble
|
||||
// up to the overlay's backdrop-close handler. Earlier symptom: clicking
|
||||
// a control next to the image edge sometimes registered as a backdrop
|
||||
// close because the click was hitting the gap between image and button.
|
||||
prevBtn.addEventListener("click", (ev) => { ev.stopPropagation(); step(-1); });
|
||||
nextBtn.addEventListener("click", (ev) => { ev.stopPropagation(); step(1); });
|
||||
closeBtn.addEventListener("click", (ev) => { ev.stopPropagation(); close(); });
|
||||
imgEl.addEventListener("click", (ev) => ev.stopPropagation());
|
||||
overlay.addEventListener("click", (ev) => {
|
||||
console.log("[lazyflat.lightbox] overlay click — target===overlay?", ev.target === overlay,
|
||||
"target=", ev.target);
|
||||
if (ev.target === overlay) close();
|
||||
});
|
||||
document.addEventListener("keydown", (ev) => {
|
||||
|
|
@ -244,34 +199,14 @@ document.addEventListener("DOMContentLoaded", openDeepLinkedFlat);
|
|||
|
||||
document.addEventListener("click", (ev) => {
|
||||
const tile = ev.target.closest(".flat-gallery-tile");
|
||||
if (!tile) {
|
||||
// DEBUG(lightbox): only log clicks somewhere INSIDE a gallery so we don't spam.
|
||||
if (ev.target.closest && ev.target.closest(".flat-gallery")) {
|
||||
console.log("[lazyflat.lightbox] click inside gallery but no .flat-gallery-tile ancestor",
|
||||
{ target: ev.target, tagName: ev.target.tagName });
|
||||
}
|
||||
return;
|
||||
}
|
||||
console.log("[lazyflat.lightbox] tile clicked", { tile, target: ev.target,
|
||||
tagName: tile.tagName, fullSrc: tile.dataset.fullSrc });
|
||||
if (!tile) return;
|
||||
const gallery = tile.closest(".flat-gallery");
|
||||
if (!gallery) {
|
||||
console.warn("[lazyflat.lightbox] tile has no .flat-gallery ancestor");
|
||||
return;
|
||||
}
|
||||
if (!gallery) return;
|
||||
ev.preventDefault();
|
||||
const tiles = Array.from(gallery.querySelectorAll(".flat-gallery-tile"));
|
||||
const list = tiles
|
||||
.map((t) => t.dataset.fullSrc || (t.querySelector("img") || {}).src || "")
|
||||
.filter(Boolean);
|
||||
console.log("[lazyflat.lightbox] gallery has", tiles.length, "tiles,", list.length, "valid urls");
|
||||
open(list, tiles.indexOf(tile));
|
||||
});
|
||||
console.log("[lazyflat.lightbox] click delegate attached");
|
||||
})();
|
||||
|
||||
// DEBUG(lightbox): catch any uncaught error so we know if app.js stopped
|
||||
// running before the IIFE attached its handlers.
|
||||
window.addEventListener("error", (ev) => {
|
||||
console.error("[lazyflat.lightbox] window error:", ev.message, ev.filename + ":" + ev.lineno);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue