From de998d59324d7ba9f250a7e6fb7eaf318cdf1ad6 Mon Sep 17 00:00:00 2001 From: Admin Date: Sun, 1 Mar 2026 20:13:04 +0500 Subject: [PATCH] fix: prevent continue-reading card from duplicating chapter label on repeated back-navigation The previous approach cloned the card DOM node and used removeChild to move it, which meant the source node was gone on re-runs and a stale clone could persist across history restores. Replace with: clear continueGrid, build a fresh element from the source card's attributes/innerHTML on every run, and hide (not remove) the original in #books-grid so it is always available as a clean source. Also scope the data-slug selector to #books-grid to avoid accidental matches against the continue-reading cards themselves. --- scraper/internal/server/ui.go | 55 +++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/scraper/internal/server/ui.go b/scraper/internal/server/ui.go index f69142f..8b8bf44 100644 --- a/scraper/internal/server/ui.go +++ b/scraper/internal/server/ui.go @@ -205,28 +205,53 @@ const homeTmpl = ` var continueGrid = document.getElementById('continue-reading-grid'); var availableHeading = document.getElementById('available-heading'); - // Clear any stale cards from a previous render (e.g. HTMX re-executing this - // script after a history restore) before re-populating. + // Always clear before re-populating so back-navigation never duplicates cards. if (continueGrid) continueGrid.innerHTML = ''; - if (inProgress.length > 0) { + if (inProgress.length > 0 && continueGrid) { var moved = 0; inProgress.forEach(function(slug) { - var card = document.querySelector('[data-slug="' + slug + '"]'); + // Find the source card in #books-grid by data-slug. + var card = document.querySelector('#books-grid [data-slug="' + slug + '"]'); if (!card) return; - var clone = card.cloneNode(true); + var chapterNum = progress[slug]; - var meta = clone.querySelector('.min-w-0'); - if (meta && chapterNum) { - var chLine = document.createElement('p'); - chLine.className = 'text-xs text-amber-400 mt-1'; - chLine.textContent = 'Chapter ' + chapterNum; - var author = meta.querySelector('p'); - if (author) author.insertAdjacentElement('afterend', chLine); - else { var title = meta.querySelector('h2'); if (title) title.insertAdjacentElement('afterend', chLine); } + + // Build a fresh card element rather than cloning + mutating, + // so repeated back-navigations cannot accumulate injected nodes. + var a = document.createElement('a'); + a.href = card.getAttribute('href'); + a.setAttribute('hx-get', card.getAttribute('hx-get')); + a.setAttribute('hx-target', card.getAttribute('hx-target')); + a.setAttribute('hx-push-url', card.getAttribute('hx-push-url')); + a.setAttribute('hx-swap', card.getAttribute('hx-swap')); + a.setAttribute('data-slug', slug); + a.className = card.className; + // Copy inner HTML then inject the chapter-progress line. + a.innerHTML = card.innerHTML; + if (chapterNum) { + var meta = a.querySelector('.min-w-0'); + if (meta) { + // Remove any previously injected progress line (defensive). + var old = meta.querySelector('.chapter-progress-line'); + if (old) old.parentNode.removeChild(old); + + var chLine = document.createElement('p'); + chLine.className = 'chapter-progress-line text-xs text-amber-400 mt-1'; + chLine.textContent = 'Chapter ' + chapterNum; + var author = meta.querySelector('p'); + if (author) author.insertAdjacentElement('afterend', chLine); + else { + var title = meta.querySelector('h2'); + if (title) title.insertAdjacentElement('afterend', chLine); + } + } } - continueGrid.appendChild(clone); - card.parentNode.removeChild(card); + htmx.process(a); + continueGrid.appendChild(a); + // Hide the original from the all-books grid (don't remove, just hide, + // so the DOM node stays available for future re-runs on the same page). + card.style.display = 'none'; moved++; }); if (moved > 0) {