← back to AsSeenInMovies
Replace pagination with infinite scrolling
c27bdce6360f6c4cfe33f0844708fdf34c02fd58 · 2026-06-23 09:33:28 -0700 · Claude
- Remove prev/next pagination nav
- Add scroll sentinel at bottom
- Load next page dynamically when user scrolls near sentinel
- Append product rows incrementally to grid
Complies with infinite scrolling requirement.
Files touched
Diff
commit c27bdce6360f6c4cfe33f0844708fdf34c02fd58
Author: Claude <claude@anthropic.com>
Date: Tue Jun 23 09:33:28 2026 -0700
Replace pagination with infinite scrolling
- Remove prev/next pagination nav
- Add scroll sentinel at bottom
- Load next page dynamically when user scrolls near sentinel
- Append product rows incrementally to grid
Complies with infinite scrolling requirement.
---
server.js | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 68 insertions(+), 7 deletions(-)
diff --git a/server.js b/server.js
index 81ebd99..031fbd1 100644
--- a/server.js
+++ b/server.js
@@ -438,12 +438,7 @@ app.get('/spotted', async (req, res) => {
${s.shop_url ? '<a class="shop-btn" href="'+esc(s.shop_url)+'" target="_blank" rel="nofollow noopener noreferrer">Where to find it ↗</a>' : ''}
</div>
</article>`).join('');
- const pager = `
- <nav class="pager" aria-label="Pagination">
- ${page > 1 ? `<a class="chip" href="/spotted?${qs({page: page-1})}">← Prev</a>` : ''}
- <span class="chip" style="background:var(--gold);color:#1a1410">Page ${page} of ${totalPages}</span>
- ${page < totalPages ? `<a class="chip" href="/spotted?${qs({page: page+1})}">Next →</a>` : ''}
- </nav>`;
+ const pager = totalPages > 1 ? `<div id="scroll-sentinel" style="padding:20px;text-align:center;color:var(--ink-faint);font-size:13px;"><span id="loading-status">Loading more…</span></div>` : '';
const body = `
<style>
.cat-bar{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:24px;padding-bottom:14px;border-bottom:1px solid var(--line)}
@@ -516,7 +511,73 @@ app.get('/spotted', async (req, res) => {
${filterBar}
${rows || '<p class="empty">No verified placements in this category yet.</p>'}
- ${pager}`;
+ ${pager}
+ <script>
+ (function(){
+ const grid = document.querySelector('main');
+ const sentinel = document.getElementById('scroll-sentinel');
+ if (!grid || !sentinel || !('IntersectionObserver' in window)) return;
+
+ let currentPage = ${page};
+ let totalPages = ${totalPages};
+ let isLoading = false;
+
+ const scrollObserver = new IntersectionObserver(entries => {
+ for (const e of entries) {
+ if (e.isIntersecting && currentPage < totalPages && !isLoading) {
+ loadNextPage();
+ }
+ }
+ }, { rootMargin: '400px 0px' });
+
+ scrollObserver.observe(sentinel);
+
+ function loadNextPage() {
+ if (isLoading || currentPage >= totalPages) return;
+ isLoading = true;
+ const nextPage = currentPage + 1;
+ const url = new URL(location.href);
+ url.searchParams.set('page', nextPage);
+
+ fetch(url.toString())
+ .then(r => r.text())
+ .then(html => {
+ const parser = new DOMParser();
+ const doc = parser.parseFromString(html, 'text/html');
+ const nextRows = doc.querySelectorAll('main > .spot-row');
+
+ if (nextRows.length === 0) {
+ currentPage = totalPages;
+ document.getElementById('loading-status').textContent = 'All placements loaded';
+ sentinel.style.color = '#999';
+ return;
+ }
+
+ const main = document.querySelector('main');
+ const lastRow = main.querySelector('.spot-row:last-of-type');
+ nextRows.forEach(row => {
+ lastRow.parentNode.insertBefore(row.cloneNode(true), sentinel);
+ });
+
+ currentPage = nextPage;
+ if (currentPage >= totalPages) {
+ document.getElementById('loading-status').textContent = 'All placements loaded';
+ sentinel.style.color = '#999';
+ } else {
+ document.getElementById('loading-status').textContent = 'Loading more…';
+ }
+
+ isLoading = false;
+ })
+ .catch(err => {
+ console.error('Failed to load more:', err);
+ document.getElementById('loading-status').textContent = 'Error loading more';
+ sentinel.style.color = '#d84315';
+ isLoading = false;
+ });
+ }
+ })();
+ </script>`;
// ItemList schema — rich snippet candidates for the gallery page.
// Each Product item links back to its parent movie page; SearchAction
// would be redundant since the site WebSite schema covers it.
← 11799d1 Add per-site favicon (kills /favicon.ico 404)
·
back to AsSeenInMovies
·
(newest)