How to Build Infinite Scrolling?

Leanne Z
JavaScript in Plain English
4 min readMay 2, 2024

--

(like Instagram, Netflix, Meta Feed)

What are we building:

  • We will learn how to build an infinite scrolling of images.
  • When we scroll to the bottom of the page, it will fetch more images.
  • We will be using a Dog API (a free API) to fetch cute dog images.
Infinite Images Scrolling

Implementation

We will be building this using plain JavaScript and HTML. You can follow step by step below. The full repo link can be found on my GitHub.

1. Outline project structure folders

-index.html
-script.js
-style.css

index.html

<!DOCTYPE html> 
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content-after-loading="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>
Create an Infinite Scroll Page using HTML CSS & JavaScript
</title>
</head>
<body>
<!-- Step 1: add container and css and script -->
<div class="container">
</div>
<script src="script.js"></script>
</body>
</html>

2. Load images in script.js

// script.js

const container = document.querySelector('.container');

async function loadImages(numImages = 9) {
for (let i=0; i < numImages; i++) {
try {
const response = await fetch('<https://dog.ceo/api/breeds/image/random>')
const data = await response.json();
const image = document.createElement('img');
image.src = data.message;
container.appendChild(image);
} catch (error) {
console.log(error);
}
}
}

loadImages();

3. Call handleScroll every time a user triggers a “scroll” event on the browser.

// script.js
...

const handleScroll = () => {
console.log('innerHeight', window.innerHeight);
console.log('scrollY', window.scrollY);
console.log('container.scrollHeight', container.scrollHeight);

if (window.innerHeight + window.scrollY >= container.scrollHeight) {
console.log('Loading more images');
loadImages();
}
}

window.addEventListener('scroll', handleScroll);

There are 3 properties that you need to know:

  1. window.innerHeight: height of what you see in your browser
  2. window.scrollY: what’s the height you scroll down already
  3. element.scrollHeight: the height of your loaded content which could overflow outside the screen

if (window.innerHeight + window.scrollY >= container.scrollHeight) , this means that we have reached the end of the content. It’s time to fetch more images.

window.innerHeight, window.scrollY, element.scrollHeight

4. Let’s add some styling to the UI

//style.css

.container {
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 10px;
justify-content: center;
}

img {
height: auto;
width: 100%;
max-width: 300px;
max-height: 300px;
object-fit: cover;
}

.loading {
max-width: 300px;
max-height: 300px;
display: flex;
}

5. Add loading spinners for a better user experience

// script.js
...

const loadingSpinner = () => {
const loadingDiv = document.createElement("img");
loadingDiv.className = "loading";
loadingDiv.src = '<https://i.gifer.com/7plQ.gif>';
container.append(loadingDiv);
}

async function loadImages(numImages = 9) {
for (let i=0; i < numImages; i++) {
try {
// Show loading spinner before fetching
loadingSpinner();

const response = await fetch('<https://dog.ceo/api/breeds/image/random>')
const data = await response.json();
// Remove loading spinner after fetching
document.querySelector('.loading').remove();

const image = document.createElement('img');
image.src = data.message;
container.appendChild(image);
} catch (error) {
console.log(error);
}
}
}

6. Wait…How can we make it better?

  1. If you keep triggering scrolling events rapidly, you will notice that it doesn’t stop fetching. This is not ideal because it will trigger too many API requests in a short period. We could improve this by using “throttling” which ensures we only fetch images at most once in 2s.
// script.js
...

const throttle = (callback, limit) => {
let lastFunc;
let lastRan;
return function() {
const args = arguments;
if (!lastRan) {
callback(...args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function() {
if ((Date.now() - lastRan) >= limit) {
callback(...args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}

const handleScroll = () => {
if (window.innerHeight + window.scrollY >= container.scrollHeight) {
loadImages();
}
}

window.addEventListener('scroll', throttle(handleScroll, 2000));

2. We can improve image loading performance by using lazy loading only when an image enters into view of the screen.

// Step 7: Optimize image loading
<img loading="lazy" src="image.jpg" alt="..." />

3. Add alt for accessibility

Anything else you can think of to improve? Comment below.

If you’d like to check out my full source code, you can access my github here. Pull requests and feedback are welcome.

>> Subscribe to @codingbff blog and my Youtube Channel

If you like this blog and wish to see more tech video tutorials, please comment below and subscribe to https://www.youtube.com/@codingbff for video updates! If I get more than 10 comments, I will upload a video blog to Youtube @codingbff.

References

In Plain English 🚀

Thank you for being a part of the In Plain English community! Before you go:

--

--