DEV Community

Cover image for Building an Infinite Scroll Component with Intersection Observer 🚀
Vikas Choubey
Vikas Choubey

Posted on

Building an Infinite Scroll Component with Intersection Observer 🚀

Demo here

In the realm of web design, user experience is paramount. Enter infinite scroll – a game-changing solution that seamlessly loads content as users scroll, eliminating the need for traditional pagination and offering a frictionless browsing experience.

scrolling
HTML Structure:

We have a <div> element with an id="content" that contains some initial items (e.g., "Item 1", "Item 2", ...).
At the end of the content, there's a sentinel <div> with the class sentinel. This sentinel div is used as a trigger point for loading more items when it comes into view.

skeleton

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Pagination with Intersection Observer</title>
  </head>

  <body>
    <div id="content">
      <div class="item">Item 1</div>
      <div class="item">Item 2</div>
      <div class="item">Item 3</div>
      <div class="item">Item 4</div>
      <div class="item">Item 5</div>
      <div class="item">Item 6</div>
      <div class="item">Item 7</div>
      <div class="item">Item 8</div>
      <div class="item">Item 9</div>
      <div class="item">Item 10</div>
      <div class="sentinel"></div>
      <!-- we will observe this element -->
    </div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

CSS Styling:

Basic styling to remove default margin and padding, and to define the appearance of items and the sentinel div. 🎨

    <style>
      body {
        font-family: Arial, sans-serif;
        margin: 0;
        padding: 0;
      }

      .sentinel {
        height: 20px;
        background-color: transparent;
      }

      .item {
        border: 1px solid #ccc;
        padding: 20px;
        margin-bottom: 10px;
      }
    </style>
Enter fullscreen mode Exit fullscreen mode

JavaScript:

javascript

  • We select the sentinel element using document.querySelector('.sentinel'). 🎯
  • We define options for the Intersection Observer:
    • root: The viewport. We set it to null to observe the document's viewport. 👀
    • rootMargin: Margin around the root. We set it to 0px. 📏
    • threshold: The percentage of the target element which should be visible before triggering the callback. We set it to 0.8, meaning the callback will be triggered when the sentinel element is 80% in view. 🚦
  • We define the handleIntersection function, which will be called when the sentinel element intersects with the viewport.
    • It checks if the sentinel element is intersecting with the viewport using entry.isIntersecting. 🛑
    • If it is intersecting, it calls the loadMoreItems function and stops observing the sentinel element. 🔄
  • We define the loadMoreItems function, which simulates loading more items:
    • It creates new <div> elements with the class item and adds them to the content area before the sentinel. 📦
    • After loading more items, it re-observes the sentinel element. 🔄
  • We create a new IntersectionObserver instance, passing in the handleIntersection function and options. 🕵️‍♂️
  • Finally, we observe the sentinel element using observer.observe(sentinel). 🚀

    <script>
        const content = document.getElementById("content");
        const sentinel = document.querySelector(".sentinel");
        const options = {
            root: null,
            rootMargin: "0px",
            threshold: 0.8, // Trigger when the sentinel is 80% visible
        };

        function handleIntersection(entries, observer) {
            entries.forEach((entry) => {
            if (entry.isIntersecting) {
                setTimeout(loadMoreItems, 500); // mimic async call
            }
            });
        }

        function addElements(min, max){
            for (let i = min; i <= max; i++) {
                const newItem = document.createElement("div");
                newItem.classList.add("item");
                newItem.textContent = "Item " + i;
                content.appendChild(newItem);
            }
        }
        function loadMoreItems() {
            // Simulate loading more items
            const len = content.children.length;
            content.removeChild(sentinel); // remove from current position
            addElements(len, len+5)
            content.appendChild(sentinel); // add before last 4 elements
            addElements(len+6, len+10)
        }

      const observer = new IntersectionObserver(handleIntersection, options);
      observer.observe(sentinel);
    </script>

Enter fullscreen mode Exit fullscreen mode

How it Works:

  • Initially, the Intersection Observer is set up to observe the sentinel element.
  • As the user scrolls down the page, when the sentinel element comes into view (i.e., when it intersects with the viewport), the handleIntersection function is called.
  • Inside handleIntersection, we load more items and stop observing the sentinel to prevent multiple triggerings.
  • After loading more items, we re-observe the sentinel so that the process can repeat when the user scrolls further down.
  • This setup allows for a smooth and efficient implementation of pagination or infinite scroll on a web page. 🌟

Note: We removed the sentinel from its previous position and added it just before the last 4 new items, this way when user scrolls down our sentinel will be in viewport before the user sees the last 4 items and we will have time to load our next new items before user reaches the end of the list, this helps us make the user experience a little better as the user never has to wait for the next new items to load.

Demo here

Connect with me on my:

LinkedIn
GitHub
Twitter

Top comments (0)