How to implement infinite scrolling in JavaScript

Written by
Published on

Infinite scrolling is a popular technique that social media sites tend to use often, mainly on mobile devices, in which a user naturally scrolls down the page while the page loads more data through an Ajax request.

There are a few reasons why you might want to avoid it however as it can cause a few unintended consequences depending on the way in which you are using it.

Regardless though, it's still a pretty cool technique to use particularly if you have a large amount of content to load and want to avoid having your site visitors click on 'load more' over and over.

Fortunately, with the scroll capture event handler in JavaScript, we can set this up with a few lines of code. So let's get started.

1. Create a dataset

First up, let's create a fair amount of test data that we can load over and over. For this use case, we'll create an array of feed item objects. Each object will just be comprised of a title property and a thumbnail image for now.

var dataset = [];

function createData(){

	for (let i = 0; i < 100; i++){
	let item = {
		title: `item ${i}`,
		image: 'https://www.thatsoftwaredude.com/images/design/g1697.png'
	}

	dataset.push(item);
	}
}

We can run the createData() function on page load.

Also we'll need to declare the following variables as well in order to track the current page that is being loaded and the number of records to display per page load.

var pagenumber = 0;
var perpage = 5;
var loadLock = false;

Each time that a new chunk of data is loaded onto the screen we will need to update the pagenumber variable by increasing it by 1. The main reason why we need to track the page number, is so that we can implement proper pagination later on.

The loadLock variable requires a bit more explanation. While a page of data is loading onto the screen, you want to prevent any future pages to load until that one page has completed rendering. The loadLock variable will be set to true once we trigger a page load, and will be set to false once again after the render function. And data will only be loaded if the lock is set to false.

2. The HTML, CSS and <template>

Because the data will be loaded through JavaScript, we can create a <template> DOM element first that will represent the HTML layout of each item in the feed.

<template id='template'>
	  <div class="item-image">
	  <img src="{{image}}">
	  </div>

<div class="item-title">{{title}}</div> </template>

And assume that the entire HTML page would resemble the following:

<html>
	<head></head>
	<body>
	<div id='items' class='items'></div>
	
	<template id='template'>
	  <div class="item-image">
	  <img src="{{image}}">
	  </div>
	  <div class="item-title">{{title}}</div>
	</template>
	</body>
</html>

We are going to be loading the dataset JSON data into the 'items' div placeholder above.

And you can use the following CSS in order to create a feed style layout.

            .items{
                width: 50%;
                margin: auto;
            }

            .items .item{
                padding: 20px;
                display:flex;
                margin: 10px;
                box-shadow: 2px 2px 4px #aaa;
                border:solid 1px #aaa;
            }

            .items .item .item-title{
                font-size: 20pt;
                font-weight:bold;
                padding: 0px 20px;
            }

            .items .item .item-image{
                width: 200px;
                overflow:hidden;
            }

            .items .item .item-image img{
                max-width: 100%;
            }

You have a large amount of options when it comes to just when to load the next page of data.

You can load the data either at the end of the current page, once the user reaches the bottom of the screen. Or you can pre-load the data before, perhaps at the 60% mark, so that users don't actually see the load action taking place.

For this example, I will pre-load the data as I think that is the typically the most popular choice that most people would want.

3. Loading data

In this particular example, loading the data will essentially come down to looping through the JSON dataset collection defined above and creating a new DOM element that will be added to a placeholder div for each item.

We don't want to load the entire collection though, just the current page that we are currently on.

And for that, we are going to use the .slice() method to only extract the data that we need for the current pagenumber.

The entire function looks like the following:

function loadData(){
     let template = document.getElementById('template');
     let pageHeader = document.createElement('div');
     let currentDataset = dataset.slice(pagenumber * perpage, (pagenumber * perpage) + perpage);

     if (currentDataset.length > 0){
        pageHeader.innerHTML = `Page ${pagenumber}`;
                                document.getElementById('items').appendChild(pageHeader);

        currentDataset.forEach(function(item){
        // create a new element with the contents of the template
        let div = document.createElement('div');

        div.className = 'item';
        div.innerHTML = template.innerHTML.replace('{{title}}', item.title)
                    .replace('{{image}}', item.image);
                    
                    document.getElementById('items').appendChild(div);
                    });
                }

                loadLock = false;
            }
}

The important part to point out is the slice() portion, which resembles the following:

let currentDataset = dataset.slice(pagenumber * perpage, (pagenumber * perpage) + perpage);

Again, we are only interested in rendering that particular portion of data. Mathematically it would resemble the following:

- (0 * 5), (0 * 5) + 5
- (1 * 5), (1 * 5) + 5
- (2 * 5), (2 * 5) + 5
- ...

The rest of the function is a relatively straight forward template replace that we will append to a parent div. And also note how the loadLock variable is reset back to false once we are done.

5. Scroll event

JavaScript allows us to attach the scroll event listener to any element that contains a scroll bar, either horizontally or vertically.

We can then access the scroll cursors location through the elements scrollY and scrollX properties.

For this particular example, we will only use the scrollY value as we are interested in top-down infinite scrolling.

The scrollY property returns back an integer value representing the current pixel location of the scrollbar, with the top of the page representing a scrollY value of 0.

window.addEventListener('scroll', function(e){
     let y = window.scrollY;
     let loadHeight = document.body.scrollHeight * .6;

     if (loadLock == false && y >= loadHeight){
        // load data
        pagenumber++;
        loadLock = true;  // locks any more loading
        loadData();
     }
});

Take note of the loadHeight variable declared.

let loadHeight = document.body.scrollHeight * .6;

The document.body.scrollHeight property essentially returns the entire height in pixels of any element. Because we want to trigger the page load at the 60% mark, we're multiplying the height by .6 to get the desired location.

Once we have scrolled to that position, if loadLock is still false then we will call the loadData() function from above to render the next portion of data.

6. Init event

And lastly, we want to create the dataset and to load the first page onto the screen on first page load.

window.addEventListener('load', function(){
     createData();
     loadData();
});

And that's it!

Infinite scrolling can be useful and can provide a better user experience, particularly on mobile devices, if implemented correctly and if doesn't take away from the overall usefulness of a website.

Check out the full running demo.

Leave a comment

No messages posted yet

Developer Poll

Q:

Add a comment

Send me your weekly newsletter filled with awesome ideas
Post