Build: Infinite Scroll
Introduction
Infinite scroll loads the next page when the user approaches the end of the current list. The modern browser primitive for this is IntersectionObserver, not a raw scroll listener.
A strong implementation prevents duplicate loads, stops when there are no more pages, renders errors without losing existing content, and exposes a cleanup method.
Why This Matters
This challenge tests browser API judgment and async state discipline. Many candidates attach a scroll handler and accidentally issue overlapping page requests. A production answer uses a sentinel element, observes intersection, and guards loading state.
Theory
Sentinel approach
Place a lightweight sentinel element after the list. When it intersects the viewport, call loadNext. The browser decides when to notify you, which is more efficient than running code on every scroll event.
State machine
The feature has three important booleans: loading, done, and sometimes error. loading prevents duplicate page requests while the sentinel remains visible. done disconnects the observer once the API says there is no next page. Existing items stay rendered if a later page fails.
API contract
A clean loadPage(page) returns { items, hasMore }. The scroller owns page numbering and rendering. The caller owns data fetching and item rendering. This separation makes the component testable and reusable.
Visual Diagrams
sentinel visible | v loading? yes -> ignore | no v load page N | +--> success with hasMore -> append, N = N + 1 +--> success without more -> append, disconnect +--> failure -> show retryable error
The loading guard is what prevents duplicate page requests.
Code Examples
Complete IntersectionObserver infinite scroller
The DOM implementation uses a sentinel element and keeps data loading separate from item rendering.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1function createPaginator(items, pageSize) {2 var page = 0;3 4 return {5 next: function () {6 var start = page * pageSize;7 var slice = items.slice(start, start + pageSize);8 page += 1;9 10 return {11 items: slice,12 hasMore: page * pageSize < items.length13 };14 }15 };16}17 18var paginator = createPaginator(['a', 'b', 'c', 'd', 'e'], 2);19console.log(paginator.next().items.join(','));20console.log(paginator.next().items.join(','));21var last = paginator.next();22console.log(last.items.join(',') + ':' + last.hasMore);Coding Exercises
Implement an IntersectionObserver infinite scroller
MediumBuild createInfiniteScroller(options).
Requirements:
- Accept
{ list, sentinel, status, loadPage, renderItem, root, rootMargin, startPage, loadImmediately }. - Use
IntersectionObserveron the sentinel. - Prevent overlapping page requests.
- Append new items without rerendering the full list.
- Stop observing when
hasMoreis false. - Keep existing content if a later page fails.
- Return
{ loadNextPage, destroy }.
Constraints:
- Do not use scroll event polling.
- Do not assume the API always succeeds.
Interview Questions
1Why is `IntersectionObserver` preferred over a scroll event listener?
IntersectionObserver is browser-optimized and evented. It avoids running JavaScript on every scroll frame and makes it easy to observe a sentinel near the bottom of the list. Scroll listeners require throttling, manual geometry calculations, and careful cleanup.
Follow-ups
- How would you support older browsers?
- How would you preserve scroll position when prepending items?
2How do you prevent duplicate page requests?
Keep a loading flag. If the sentinel fires again while a page request is in flight, return early. Clear the flag in finally so both success and failure unblock future attempts.
Quiz
1. What should happen when the API returns `hasMore: false`?
Summary
- Use a sentinel plus `IntersectionObserver` for modern infinite scroll.
- `loading` prevents overlapping requests; `done` stops observation after the final page.
- Append new page nodes instead of rerendering the whole list.
- Keep loading concerns separate from item rendering.
Cheat Sheet
Infinite scroll checklist
- Sentinel after the list.
IntersectionObserverwith helpfulrootMargin.loadingguard for duplicate intersections.doneguard for final page.- API contract:
{ items, hasMore }. - Append only new nodes.
- Show status and preserve existing items on error.
- Return
destroy().