Scrolling to infinity


In this article, we're going to scroll to infinity (and beyond). That might take a while, though, so as you start scrolling I'm going to explain how to implement an infinitely scrollable page. If you don't care how it works and just want to scroll straight to infinity, scroll right ahead.


For finite beings, infinity is always out-of-reach. You can't touch it, you can't hold it, you can't even wrap your head around it most of the time.

But the dawn of the internet age has allowed us to do the impossible: create portals to infinity on everyone's phones and laptops.

I'm talking about the infinite scroll: an interface that allows a user to scroll through an endless stream of content that is continually refreshed and added to as they progress through it. Examples include the content feeds of popular social media apps like Facebook, Twitter, and TikTok.

Physicists and mathematicians are still marveling at how it's possible, and, truth be told, many of us are so mesmerized that we haven't put down our phones since discovering it.

Go ahead, you can put your phone down. The finite world isn't all that bad.

But before you do, at least try to finish this article because I'm going to explain how you, too, can create an infinitely scrollable page.

Except first I need to let you in on a dirty little secret: the infinite scroll is not, in fact, infinite. It's just a trick.

One simple trick to achieve infinity

Here's how it works.

You start with a web page of some finite length.

Then you set some scroll threshold—such as the bottom of the page or maybe within one screen-length of the bottom—that, when the user scrolls passed it, triggers a function that fetches more content for the user to scroll through. Then you append that new content to the page so the user can keep scrolling. If you do it fast enough, they may not even notice a thing.

Then when they get to the bottom of the extended page, you do the same thing: fetch more content, add that content to the page, and so on.

That's enough theory. Let's put it into practice.

Putting infinity into practice

First, we need to determine what kind of content the user will be scrolling through. If this was a video-sharing app then the content would be videos, if it was text-based social media, like Twitter, then it'd be text content.

I'm not a social media company, though, so I don't have any content per se. We could just scroll infinitely through a blank white page, but that might get boring.

I like pretty colors, so instead let's scroll infinitely through some fun colors.

This makes implementing the infinite scroll easier since we don't need to fetch any data each time the user is close to scrolling to the end. We just need to create a new generic HTML element (div), set it's background color to whatever we want, and append it to the page.

We can call these rectangles, since they'll basically just be colored rectangles roughly the width and height of the user's screen.

At first, we can start with just two rectangles on the page.

Then we can use the Intersection Observer API to trigger an event when our bottom-most rectangle is visible on screen. The Intersection Observer is browser feature that fires events when elements of a page cross into and out of the viewport—the portion of the page visible on the user's screen.

In response to that event, we'll make a new rectangle, append it to the page, and tell the Intersection Observer to trigger an event when that new rectangle is visible on screen.

We'll keep repeating this process infinitely, or until the user reaches some preset maximum number of rectangles so they don't waste their whole life scrolling.

Now let's put this into code.

Putting infinity into code

We'll start out with 2 rectangles in the body of our page's HTML—the rest will be added with JavaScript as the user scrolls.

I'm using Tailwind CSS classes for shorthand: w-full sets the width to 100% while h-screen sets the height to be 100vh or 100% of the viewport height. bg-green-300 and bg-blue-600 are green and blue background colors for the starting rectangles.


            <body>
            <div id="rectangle1" class="w-full h-screen bg-green-300"></div>
            <div id="rectangle2" class="w-full h-screen bg-blue-600"></div>
            </body>
            
            

We need some sort of global "state" to keep track how many rectangles are currently on the page, and our next "target" element that, when visible on screen, will trigger adding a new rectangle.

We're starting with 2 rectangles on screen so the starting number of elements is 2, and our first target element is rectangle2 (the id of the rectangles will just be "rectangle" + the number rectangle that it is). Thus our state starts out looking like this:

const state = {
                nextTargetElement: 'rectangle2',
                numElements: 2,
            }
            

Next we can set up our Intersection Observer that will fire events when our target rectangle is visible on screen.

We can create it with the IntersectionObserver contructor which takes the callback function to be run when an intersection is observed (called here intersectionCallback) and some basic options as arguments.

We add targets to observe by using the observe method on the Intersection Observer. The first element we'll want to observe is rectangle2 so I add that to the intersection observer below.

Altogether, setting up the Intersection Observer looks like this:

let intersectionObserver;
            const setupIntersectionObserver = () => {
                const options = {
                    rootMargin: "0px",
                    threshold: 0,
                };
            
                intersectionObserver = new IntersectionObserver(intersectionCallback, options);
            
                const target1 = document.querySelector("#rectangle2");
            
                intersectionObserver.observe(target1);
            }
            setupIntersectionObserver();
            
            

Now we need to create that intersectionCallback function to be run when a new intersection is observed (which can happen when an observed element enters or leaves the viewport aka the screen).

Inside that callback, we'll loop through the entries—the targets we're currently observing—and if we find that an entry is isIntersecting—that is, it's currently at least partially visible on screen—and it has the same id as our next target rectangle, the bottom rectangle on the page, then we'll trigger the addNewContent function.

We can also "unobserve" that element since we now only care when the next rectangle is visible.

Altogether, that function looks like this:

const intersectionCallback = (entries) => {
                entries.forEach((entry)=> {
                    if (entry.isIntersecting && entry.target.id==state.nextTargetElement) {
                        addNewContent();
                        intersectionObserver.unobserve(entry.target);
                    }
                });
            }
            

Next we need to create the addNewContent function for adding a new rectangle to the page.

In that function we'll increment the total number of rectangles, create a new random rectangle color, and then append a new div element to the bottom of the page using the appendChild method.

We'll set the id of the new element to be 'rectangle' + the number of rectangles, set its backgroundColor style property to be the randomly generated color, and set its width and height to be the dimensionse of the screen (i.e. window.innerWidth and window.innerHeight).

Finally, we'll tell the Intersection Obsever to observe this newly created rectangle and set our next target rectangle to be this rectangle's id.

That function ends up looking like this:

const addNewContent = () => {
                state.numRectangles +=1;
            
                const rectangleId = 'rectangle' + String(state.numRectangles);
                const rectangleColor = 'rgb(' + String(Math.round(255*Math.random())) + ',' + String(Math.round(255*Math.random())) + ',' + String(Math.round(255*Math.random())) + ')';
            
                const newRectangle = document.body.appendChild(document.createElement("div"));
                newRectangle.id = rectangleId;
                newRectangle.style.backgroundColor = rectangleColor;
                newRectangle.style.width = '100%';
                newRectangle.style.height = '100vh';
            
                intersectionObserver.observe(newRectangle);
            
                state.nextTargetElement = rectangleId;
            
            }
            

That's all it takes to create an infinitely scrollable page! You can find the code for a basic infinite scroll demo on GitHub.

I also added a few final touches to the below infinite scroll.

Since it can feel more "natural" to have a slight delay before loading new content, I added a delay and a small loading indicator before adding new rectangles. In a more conventional infinite scroll, the delay would come from fetching or processing additional content from the server.

I also added the option to set a color palette if you don't want to scroll through random colors.

It's fun to have a little animation so I made it so the next rectangle seems to spring up after it's added if there's a delay.

Finally, I don't want you actually scrolling forever (there's better ways to spend eternity), so I set a max number of elements you can scroll through. Feel free to adjust it using the slider below.

Keep scrolling down to experience the infinite scroll.

100