← back to Bleachresistantfabrics
fix: wire IntersectionObserver infinite scroll so all 600 products are reachable
9001c5d4b193afad86b096f1c01e51442e86ee59 · 2026-05-30 21:34:19 -0700 · Steve Abrams
#more sentinel div existed but was never observed — users could only see
36 of 600 products. Added IntersectionObserver on #more, loading/exhausted
guards, and page increment logic matching the corkwallcovering reference pattern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files touched
Diff
commit 9001c5d4b193afad86b096f1c01e51442e86ee59
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat May 30 21:34:19 2026 -0700
fix: wire IntersectionObserver infinite scroll so all 600 products are reachable
#more sentinel div existed but was never observed — users could only see
36 of 600 products. Added IntersectionObserver on #more, loading/exhausted
guards, and page increment logic matching the corkwallcovering reference pattern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
public/index.html | 31 ++++++++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/public/index.html b/public/index.html
index fd60afa..8b7cd49 100644
--- a/public/index.html
+++ b/public/index.html
@@ -435,9 +435,10 @@ document.getElementById('yr').textContent = new Date().getFullYear();
})();
// Catalog state
-const state = { q: '', rail: 'all', sort: 'newest', page: 1, limit: 36, total: 0 };
+const state = { q: '', rail: 'all', sort: 'newest', page: 1, limit: 36, total: 0, pages: 1, loading: false, exhausted: false };
const $grid = document.getElementById('grid');
const $stat = document.getElementById('stat');
+const $more = document.getElementById('more');
// Persisted UI
(function(){
@@ -496,20 +497,44 @@ function card(p) {
}
async function load(reset) {
- if (reset) $grid.innerHTML = '';
- $stat.textContent = 'Loading…';
+ if (reset) {
+ $grid.innerHTML = '';
+ state.page = 1;
+ state.exhausted = false;
+ $more.style.display = 'none';
+ }
+ if (state.loading || state.exhausted) return;
+ state.loading = true;
+ if (state.page === 1) $stat.textContent = 'Loading…';
+ $more.style.display = 'block';
const params = new URLSearchParams({ q: state.q, rail: state.rail, sort: state.sort, page: state.page, limit: state.limit });
try {
const r = await fetch('/api/products?' + params);
const j = await r.json();
state.total = j.total;
+ state.pages = j.pages;
j.items.forEach(p => $grid.insertAdjacentHTML('beforeend', card(p)));
$stat.textContent = j.total + ' fabrics · page ' + j.page + ' of ' + j.pages;
+ if (state.page >= j.pages || j.items.length === 0) {
+ state.exhausted = true;
+ $more.style.display = 'none';
+ } else {
+ state.page++;
+ $more.style.display = 'block';
+ }
} catch (e) {
$stat.textContent = 'Failed to load catalog. Refresh to retry.';
+ $more.style.display = 'none';
}
+ state.loading = false;
}
+// Infinite scroll via IntersectionObserver — fires load() when #more scrolls into view
+const io = new IntersectionObserver(entries => {
+ for (const e of entries) if (e.isIntersecting && !state.exhausted) load(false);
+}, { rootMargin: '600px 0px' });
+io.observe($more);
+
load(true);
</script>
<!-- DW-HAMBURGER-NAV v1 (canonical 2026-05-10) -->
← 90dd291 feat(fleet): theme1/theme2 query-param-gated toggle on catal
·
back to Bleachresistantfabrics
·
fix /api/rails facet counts to honor active q/vendor filters e8ec1f4 →