Building an Infinite Scroll Component with Intersection Observer 🚀
Tags: html, webdev, javascript, css
Published: 2024-03-02 14:06:56


[Demo here](https://codepen.io/vikas2426/pen/rNbamba)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fxu9q7nsh5aim910v2b.gif)
**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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9eep88yqzfxyw82dptng.png)
```html
<!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>
```

**CSS Styling:**

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

```css
    <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>
```

**JavaScript:**

![javascript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggp35p6iks2rm295pq4b.gif)
- 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)`. 🚀

```js

    <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>

```

### 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](https://codepen.io/vikas2426/pen/rNbamba)

## Connect with me on my:
[LinkedIn](https://www.linkedin.com/in/vikas-choubey/)
[GitHub](https://github.com/Vikas2426/)
[Twitter](https://twitter.com/itsvikas7/)