fix: prevent continue-reading card from duplicating chapter label on repeated back-navigation
Some checks failed
CI / Lint (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled

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 <a>
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.
This commit is contained in:
Admin
2026-03-01 20:13:04 +05:00
parent 5b527919f6
commit de998d5932

View File

@@ -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) {
// 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 = 'text-xs text-amber-400 mt-1';
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); }
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) {