Now Brief Gives You A Heads Up to Stop Looking at Phone While Walking
Perhaps it's time to unlearn some old habits!
/** * SammyGuru — Infinite Scroll Feed * * Watches a sentinel div with IntersectionObserver. When it comes into * view (roughly 400px before the bottom of the feed), fetch the next * page of posts via AJAX and append them to the grid. * * Also handles tab clicks to filter by category — resets the feed and * fetches page 1 of the new category. * * Depends on: sgFeed global (ajaxUrl + nonce) localized from PHP. */ (function () { 'use strict'; if (typeof sgFeed === 'undefined') { return; } // ── Initialize every feed on the page ───────────────────────── function initAllFeeds() { document.querySelectorAll('.sg-feed').forEach(initFeed); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initAllFeeds); } else { initAllFeeds(); } // ── Tab click handler (delegated, so it works for all feeds) ── document.addEventListener('click', function (e) { var tab = e.target.closest('.sg-feed-tab'); if (!tab) return; var container = tab.closest('.sg-feed-container'); if (!container) return; var feed = container.querySelector('.sg-feed'); if (!feed) return; // Update tab styles container.querySelectorAll('.sg-feed-tab').forEach(function (t) { t.style.background = '#f0f2f5'; t.style.color = '#555'; t.classList.remove('sg-feed-tab--active'); }); tab.style.background = '#19289B'; tab.style.color = '#fff'; tab.classList.add('sg-feed-tab--active'); // Update feed state feed.dataset.category = tab.dataset.category || ''; feed.dataset.currentPage = '1'; feed.dataset.exhausted = 'false'; var endMsg = feed.querySelector('.sg-feed-end'); if (endMsg) endMsg.style.display = 'none'; // Fetch page 1 of the new category (replace existing posts) loadFeedPage(feed, true); }); // ── Initialize a single feed's IntersectionObserver ─────────── function initFeed(feed) { // Guard against double-init (mutation observer + DOMContentLoaded races) if (feed.dataset.sgInit === '1') return; feed.dataset.sgInit = '1'; var sentinel = feed.querySelector('.sg-feed-sentinel'); if (!sentinel) return; // Fallback for browsers without IntersectionObserver if (typeof IntersectionObserver === 'undefined') { return; } var observer = new IntersectionObserver(function (entries) { var entry = entries[0]; if (!entry.isIntersecting) return; if (feed.dataset.exhausted === 'true') return; if (feed.dataset.loading === 'true') return; loadFeedPage(feed, false); }, { // Very aggressive — starts fetching 2000px before the user reaches the end, // which means load finishes well before they see the bottom rootMargin: '2000px 0px', threshold: 0, }); observer.observe(sentinel); feed._observer = observer; // EAGER PREFETCH: immediately start fetching page 2 after the page // finishes initial render. This way the first scroll-to-load feels instant // because the network request has been running in the background. var prefetchDelay = 600; // wait for initial paint to settle setTimeout(function () { if (feed.dataset.prefetched === '1') return; // one-shot if (feed.dataset.exhausted !== 'true' && feed.dataset.loading !== 'true') { feed.dataset.prefetched = '1'; loadFeedPage(feed, false); } }, prefetchDelay); } // ── Fetch + append (or replace) a page of posts ─────────────── function loadFeedPage(feed, replace) { if (feed.dataset.loading === 'true') return; feed.dataset.loading = 'true'; var loading = feed.querySelector('.sg-feed-loading'); var grid = feed.querySelector('.sg-feed-grid'); var endMsg = feed.querySelector('.sg-feed-end'); if (loading) loading.style.display = 'block'; var page = parseInt(feed.dataset.currentPage || '1', 10); if (replace) { page = 1; } else { page += 1; } var formData = new FormData(); formData.append('action', 'sg_load_feed'); formData.append('nonce', sgFeed.nonce); formData.append('page', page); formData.append('per_page', feed.dataset.perPage || '6'); formData.append('category', feed.dataset.category || ''); fetch(sgFeed.ajaxUrl, { method: 'POST', credentials: 'same-origin', body: formData, }) .then(function (response) { if (!response.ok) throw new Error('HTTP ' + response.status); return response.json(); }) .then(function (result) { if (result && result.success && result.data && result.data.html) { if (replace && grid) { grid.innerHTML = result.data.html; } else if (grid) { grid.insertAdjacentHTML('beforeend', result.data.html); } feed.dataset.currentPage = page; } else { feed.dataset.exhausted = 'true'; if (replace && grid) { grid.innerHTML = '
No posts in this category yet.
'; } else if (endMsg) { endMsg.style.display = 'block'; } } }) .catch(function (err) { console.error('[sg-feed] load error:', err); // Don't mark as exhausted on network errors — let user retry via scroll }) .finally(function () { if (loading) loading.style.display = 'none'; feed.dataset.loading = 'false'; }); } })();July 2026 Unpacked Mystery Box
SammyGuru may earn a commission on purchases made through links on this page. This helps support our editorial team at no extra cost to you.