INFINITE SCROLL in JAVASCRIPT(SCROLL EVENT) - Part 1
Infinite scroll is user interface pattern when new data keeps loading as user scrolls down the page instead of traditional pagination where user has to click "Next" button to go to next page.
I've mentioned few examples of the infinite scroll below:
There are other use cases as well where infinite scroll is used. but let's come to the point and start building the infinite scroll component.
For example purpose I will be using star wars API (https://swapi.dev/api/people/) for the implementation. It is a public API anyone can access. This API fetches the people associated with star wars like actors, directors etc.
index.html: In this file we are referencing our javascript and css files and also it has div with a class of artist-container. In general I'm naming it as artist-container because it will have list of people from star wars.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="styles.css" />
<script src="index.js" defer></script>
</head>
<body>
<div class="artist-container"></div>
</body>
</html>
styles.css: The css file has style for artist-container and artist. artist will be a <p> tag that we will add as a child to artist-container component. Now let's jump to important part where all the logic lies..
body {
background-color: white;
color: white;
}
h1 {
text-align: center;
}
.artist-container {
width: 75ch;
height: 75vh;
overflow-y: scroll;
margin: auto;
padding: 10px;
border-radius: 12px;
background-color: #001119;
}
.artist {
font-size: 18px;
margin: 16px;
padding: 10px;
border-top: 4px solid #11967e;
border-radius: 8px;
background-color: black;
}
index.js: Let's see step by step what's happening in this file
Recommended by LinkedIn
count: 82
next: "https://swapi.dev/api/people/?page=2"
previous: null
results:
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
2. canFetchData flag: The scroll event fires continuously as we scroll through the page so when we already called the API requesting the next page then we would want to hold until we get the response and then make the request further. that''s why using this flag so that we wait for the response of the existing API call.
3. Then we just get the container and attach the scroll event to it. Now the handleScroll handler has some height calculations which I took from google. It calculates the leftover space which is yet to be scrolled. so the if condition checks if user has scrolled all the way down or not then call the fetchData function. otherwise return.
4. fetchData: In this function we call the API and we create the fragment and append elements to this fragment instead of directly appending it to the artist-container. we do this because of the performance. If every time we update the container directly then it will be costly. Also if there's no next page we just remove the eventListener.
5. The createStarElement and createApiURL are self explainatory functions.
const BASE_URL = "https://swapi.dev/api/people/";
let nextId = null; // will be using to get the next page data if exist
let canFetchData = true; // the scroll event fires continously as we scroll
const container = document.querySelector(".artist-container");
container.addEventListener("scroll", handleScroll);
fetchData();
function handleScroll() {
if (!canFetchData) {
return;
}
const bottomLeftToScroll =
this.scrollHeight - this.scrollTop - this.clientHeight;
if (bottomLeftToScroll > 0) {
return;
}
fetchData();
}
async function fetchData() {
canFetchData = false;
const url = createApiURL();
const response = await fetch(url);
const data = await response.json();
const { results, next } = data;
const fragment = document.createDocumentFragment();
results.forEach(({ name }) => {
fragment.appendChild(createStarElement(name));
});
container.appendChild(fragment);
if (next) {
nextId = next[next.length - 1];
} else {
container.removeEventListener("scroll", handleScroll);
}
canFetchData = true;
}
function createStarElement(name) {
const element = document.createElement("p");
element.textContent = name;
element.classList.add("artist");
return element;
}
function createApiURL() {
const url = new URL(BASE_URL);
if (nextId != null) {
url.searchParams.set("page", nextId);
}
return url;
}
That's all in this article, obviously there is performance overhead to this solution. Because the scroll event gets triggered continuously as user scrolls through the page. We can also use Intersection Observer API which is more efficient than this approach. I will be covering that in part 2 of this article.
Thank you,
Mohammad Izhan