← back to Wallco Ai
/designs: infinite scroll, replace pagination links
65dcd74741cff1011676fa63ecef847aafec0521 · 2026-05-13 08:31:44 -0700 · SteveStudio2
Steve standing rule: every site needs infinite scrolling. Was clicking
through 41 numbered pages.
- Grid carries data-current-page / data-total-pages / data-total
- Below the grid: infinite-sentinel (IntersectionObserver target with
600px rootMargin so loading kicks in before the user hits the bottom)
- infinite-status pill below: 'loading page N of 41' → 'scroll for
page N+1' → 'all 2429 designs loaded' at the end
- Fetches same /designs URL with page+1, DOMParser-parses the response,
appends only the children of #catalog-grid to the existing grid
- Single-flight (loading guard), increments cursor on success, disconnects
observer when last page reached. <noscript> fallback shows original
pager so JS-disabled clients still navigate.
- Works from any starting page — Steve's URL ?page=16 will load 17..41
as he scrolls.
Smoke-tested: HTML renders infinite-sentinel + script; 41 pages × 60
per page = 2429 designs total ready to stream.
Files touched
Diff
commit 65dcd74741cff1011676fa63ecef847aafec0521
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 08:31:44 2026 -0700
/designs: infinite scroll, replace pagination links
Steve standing rule: every site needs infinite scrolling. Was clicking
through 41 numbered pages.
- Grid carries data-current-page / data-total-pages / data-total
- Below the grid: infinite-sentinel (IntersectionObserver target with
600px rootMargin so loading kicks in before the user hits the bottom)
- infinite-status pill below: 'loading page N of 41' → 'scroll for
page N+1' → 'all 2429 designs loaded' at the end
- Fetches same /designs URL with page+1, DOMParser-parses the response,
appends only the children of #catalog-grid to the existing grid
- Single-flight (loading guard), increments cursor on success, disconnects
observer when last page reached. <noscript> fallback shows original
pager so JS-disabled clients still navigate.
- Works from any starting page — Steve's URL ?page=16 will load 17..41
as he scrolls.
Smoke-tested: HTML renders infinite-sentinel + script; 41 pages × 60
per page = 2429 designs total ready to stream.
---
server.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 72 insertions(+), 2 deletions(-)
diff --git a/server.js b/server.js
index 4e3e532..a57bc0a 100644
--- a/server.js
+++ b/server.js
@@ -2910,17 +2910,87 @@ ${cat === 'stoned-animals' ? `
</div>` : ''}
</div>
- <div class="design-grid catalog-grid" id="catalog-grid">
+ <div class="design-grid catalog-grid" id="catalog-grid" data-current-page="${page}" data-total-pages="${pages}" data-total="${total}">
${cards || '<p class="empty-state">No designs found.</p>'}
</div>
- ${pages > 1 ? `<div class="pager">${pagerLinks}</div>` : ''}
+ ${pages > 1 ? `
+ <div id="infinite-sentinel" style="height:1px;width:100%"></div>
+ <div id="infinite-status" style="text-align:center;padding:24px 0;font:11px/1 var(--sans,system-ui);letter-spacing:.18em;text-transform:uppercase;color:var(--ink-faint,#888)">${page < pages ? `loading more · page ${page} of ${pages}` : `all ${total} designs loaded`}</div>
+ <noscript><div class="pager">${pagerLinks}</div></noscript>
+ ` : ''}
</section>
</main>
${FOOTER}
<script type="application/ld+json">${schema}</script>
<script>
+// ── Infinite scroll — fetch successive /designs?page=N HTML pages, extract
+// just the design-card children, append to the grid when the sentinel
+// approaches the viewport. Single-flight + page cursor + done-state.
+(function(){
+ var grid = document.getElementById('catalog-grid');
+ var sentinel = document.getElementById('infinite-sentinel');
+ var statusEl = document.getElementById('infinite-status');
+ if (!grid || !sentinel || !statusEl) return;
+ var currentPage = parseInt(grid.dataset.currentPage || '1', 10);
+ var totalPages = parseInt(grid.dataset.totalPages || '1', 10);
+ if (currentPage >= totalPages) return; // already on last page
+ var loading = false;
+
+ function nextUrl() {
+ var u = new URL(window.location.href);
+ u.searchParams.set('page', String(currentPage + 1));
+ return u.toString();
+ }
+
+ function appendCards(html) {
+ var doc = new DOMParser().parseFromString(html, 'text/html');
+ var nextGrid = doc.getElementById('catalog-grid');
+ if (!nextGrid) return 0;
+ var added = 0;
+ Array.from(nextGrid.children).forEach(function(node){
+ grid.appendChild(node);
+ added++;
+ });
+ return added;
+ }
+
+ function loadMore() {
+ if (loading) return;
+ if (currentPage >= totalPages) {
+ statusEl.textContent = 'all ' + (grid.dataset.total||'') + ' designs loaded';
+ return;
+ }
+ loading = true;
+ statusEl.textContent = 'loading page ' + (currentPage + 1) + ' of ' + totalPages + '…';
+ fetch(nextUrl(), { credentials: 'same-origin' })
+ .then(function(r){ return r.text(); })
+ .then(function(html){
+ var n = appendCards(html);
+ currentPage++;
+ if (n === 0) {
+ // no more rows — stop polling
+ statusEl.textContent = 'all ' + (grid.dataset.total||'') + ' designs loaded';
+ io.disconnect();
+ } else if (currentPage >= totalPages) {
+ statusEl.textContent = 'all ' + (grid.dataset.total||'') + ' designs loaded';
+ io.disconnect();
+ } else {
+ statusEl.textContent = 'scroll for page ' + (currentPage + 1) + ' of ' + totalPages;
+ }
+ loading = false;
+ })
+ .catch(function(){ statusEl.textContent = 'load error — scroll to retry'; loading = false; });
+ }
+
+ var io = new IntersectionObserver(function(entries){
+ entries.forEach(function(e){ if (e.isIntersecting) loadMore(); });
+ }, { rootMargin: '600px 0px 600px 0px' });
+ io.observe(sentinel);
+})();
+</script>
+<script>
(function(){
var slider = document.getElementById('density-slider');
var grid = document.getElementById('catalog-grid');
← e54cb62 pairings: Dunn-Edwards + Sherwin-Williams paint chip suggest
·
back to Wallco Ai
·
daemons: max-it mode — default cadence 240s → 90s ed965df →