Skip to Content
Course content

215: Practice Exercise: Building an Infinite Scroll List

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I remember a project a few years back where a junior dev on my team was tasked with building a "News Feed" for a client. He went with the most intuitive approach: he added an event listener to the window's scroll event. Every time the user scrolled a single pixel, the browser ran a heavy calculation to check if the user had reached the bottom. The result? The page felt sluggish, the laptop fans started screaming, and the site practically froze on mobile devices. He had accidentally created a performance bottleneck by firing a function hundreds of times per second.

The problem is that the scroll event is synchronous and fires constantly. If you're doing anything complex inside that listener—like calculating offsetHeight or triggering an API call—you're going to kill the frame rate. In modern JavaScript, we have a much more elegant tool for this: the IntersectionObserver API. Instead of asking the browser "Where am I right now?" every millisecond, we tell the browser "Let me know when this specific element becomes visible."

Taming the scroll with IntersectionObserver

The IntersectionObserver is a game-changer because it offloads the visibility logic to the browser's own optimized internals. You create an observer, give it a callback function, and then tell it which element to watch. When that element enters the viewport (or a specific percentage of it does), the callback fires.

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log("Bottom reached! Load more data...");
      loadMoreContent();
    }
  });
}, { threshold: 1.0 });

// We tell the observer to watch a specific 'sentinel' element
observer.observe(document.querySelector('#scroll-anchor'));

I usually recommend using a "sentinel" element—a simple, empty div placed at the very bottom of your list. When that div scrolls into view, it's your signal to fetch the next page of data. It's cleaner than calculating coordinates and far more performant.

Preventing the Double-Fetch Glitch

One thing that always trips people up is the "double-fetch." Because the observer might trigger slightly before the new content is fully rendered, or because the user is scrolling quickly, you might accidentally fire off three API requests for the same page of data. This leads to duplicate items in your list and a messy UI.

The fix is simple: use a loading flag. I always wrap my fetch logic in a boolean check. If isLoading is true, the function returns immediately. Only once the data is fetched and appended to the DOM do we set isLoading back to false. It’s a small addition, but it prevents your API from getting hammered and your UI from glitching.

Managing the DOM Append

When you get your new batch of data, you'll be tempted to use innerHTML += .... Don't do it. That forces the browser to re-parse and re-render the entire list every time you add a few items, which defeats the purpose of a performant infinite scroll. Instead, create the elements using document.createElement or use a DocumentFragment to batch your updates. This keeps the browser's main thread happy and the scrolling smooth.




📋 Practical Task

Exercise: Building the Endless Portrait Gallery

Your goal is to create a gallery that loads "portraits" (images) as the user scrolls. You will use a mock API simulation to mimic paginated data fetching.

  • Create an HTML structure with a container #gallery and a sentinel element #sentinel at the bottom.
  • Implement an IntersectionObserver that triggers a function called fetchImages() whenever the #sentinel enters the viewport.
  • Inside fetchImages(), implement a loading flag (e.g., isFetching = true) to prevent duplicate requests while a "network call" is in progress.
  • Simulate an API call using a setTimeout of 1 second. The "API" should return an array of 10 image URLs (you can use https://picsum.photos/200/300?random=X where X is a number).
  • Append these images to the gallery using a DocumentFragment to ensure high performance.
  • Once the images are appended, reset your loading flag so the next scroll trigger can work.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.