What is Infinite Scroll?
Infinite scroll is a UI pattern that automatically loads additional content as the user reaches the end of the currently visible list, creating a seamless, seemingly endless browsing experience.
Why Avoid Frameworks?
Using a full‑stack framework for a single scrolling feature can introduce unnecessary bloat, slower load times, and reduced control over performance optimizations.
- Performance: Smaller payloads mean faster initial page render.
- Maintainability: Vanilla code is easier to audit and modify without framework abstractions.
- Portability: The solution works in any environment that supports modern JavaScript.
How to Build Infinite Scroll with Generators
Core Concepts
- Generators: Functions that can pause execution and yield values on demand, perfect for incremental data fetching.
- IntersectionObserver API: Efficiently detects when a sentinel element enters the viewport, triggering the next data load.
- Lazy Rendering: Append new items only after they are fetched, keeping the DOM lightweight.
Step‑by‑Step Implementation
- 1. Create a data generator:
function* fetchPages(pageSize){ let page = 1; while (true) { const response = await fetch(`/api/items?page=${page}&size=${pageSize}`); const data = await response.json(); if (data.length === 0) break; yield data; page++; } } - 2. Set up the sentinel element: Add an empty
<div id="sentinel"></div>at the bottom of the content container. - 3. Initialize IntersectionObserver:
const observer = new IntersectionObserver(handleIntersect, { rootMargin: '200px', threshold: 0 }); observer.observe(document.getElementById('sentinel')); - 4. Define the intersection handler:
const generator = fetchPages(20); async function handleIntersect(entries) { for (const entry of entries) { if (entry.isIntersecting) { const {value: items, done} = await generator.next(); if (done) { observer.unobserve(entry.target); return; } renderItems(items); } } } - 5. Render items to the DOM:
function renderItems(items) { const container = document.getElementById('list'); items.forEach(item => { const el = document.createElement('div'); el.className = 'item'; el.textContent = item.title; container.appendChild(el); }); }
Handling Edge Cases
- End of Data: Stop observing the sentinel when the generator reports
done. - Error Recovery: Wrap fetch calls in try/catch and display a retry button on failure.
- Accessibility: Provide a fallback “Load more” button for users with reduced motion preferences or when IntersectionObserver is not supported.