[object Object]

← 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 →