13
Easily implement Infinite Scrolling using Intersection Observer in vanilla JavaScript
It is when more items keep loading as we scroll. Below is a GIF for visual representation.
Difficult way to do it (using getBoundingClientRect
)
Here we are discussing the difficult way in brief. It is to build up are appreciation for the better method (i.e Intersection Observer).
To implement infinite scrolling we would have to add a scroll
event listener to a HTML Element. This will fire a callback every time the element gets scrolled.
In the callback we can use getBoundingClientRect to determine the relative position of any element. getBoundingClientRect returns us the left
, top
, right
, bottom
property of the element.
Using left
, top
, right
, bottom
we can determine if the element is in our view port. If it is then we can add more items to our list or do perform some other action.
Here is a demo of getBoundingClientRect
function.
- Complicated and requires to write more code. Hence, more chances of bugs.
- Performance issues: The callback fires with each scroll and computes the logic. It all also runs on the main thread.
Easy way to do it (using IntersectionObserver
)
Using IntersectionObserver we can observe any target element for its intersection with another element or the viewport. In simpler words, if the target element enters or leaves our viewport then a callback will be fired.
Note - Old browsers may not support the IntersectionObserver API. Check the latest support at MDN.
<main id="main">
<h2>List of Random Names</h2>
<ul id="ul"></ul>
<button id="load">Load more names</button>
</main>
We have a unordered list (ul
) and a button
at the end of the list. This is how the above HTML structure looks when rendered.
We will add an IntersectionObserver over the 'load more names' button. Whenever the button enters our view port a callback will be fired which will add li
tags under the existing ul
tags.
const loadBtn = document.getElementById('load')
// Observe loadBtn
const options = {
// Use the whole screen as scroll area
root: null,
// Do not grow or shrink the root area
rootMargin: "0px",
// Threshold of 1.0 will fire callback when 100% of element is visible
threshold: 1.0
};
const observer = new IntersectionObserver((entries) => {
// Callback to be fired
// Entries is a list of elements out of our targets that reported a change.
entries.forEach((entry) => {
// Only add to list if element is coming into view not leaving
if (entry.isIntersecting) {
// Perform some operation here
}
});
}, options);
observer.observe(loadBtn);
For detailed information on the options to IntersectionObserver visit the MDN page.
Using the above code we have added an observer to observe the load button. Now we need a function to fetch and add items to the list.
We will call this inside the callback.
async function addNamesToList(ul_id, num) {
const ul = document.getElementById(ul_id);
// Mock func to return array of dummy names
const names = await mockAPI(num);
// For each name in names append it to the ul element
names.forEach((name) => {
const li = document.createElement("li");
li.innerText = name;
ul.appendChild(li);
});
}
import mockAPI from "./mockAPI";
const loadBtn = document.getElementById("load");
async function addNamesToList(ul_id, num) {
const ul = document.getElementById(ul_id);
// Mock func to return array of dummy names
const names = await mockAPI(num);
// For each name in names append it to the ul element
names.forEach((name) => {
const li = document.createElement("li");
li.innerText = name;
ul.appendChild(li);
});
}
(function () {
addNamesToList("ul", 50);
// Observe loadBtn
const options = {
// Use the whole screen as scroll area
root: null,
// Do not grow or shrink the root area
rootMargin: "0px",
// Threshold of 1.0 will fire callback when 100% of element is visible
threshold: 1.0
};
const observer = new IntersectionObserver((entries) => {
// Callback to be fired
entries.forEach((entry) => {
// Only add to list if element is coming into view not leaving
if (entry.isIntersecting) {
addNamesToList("ul", 10);
}
});
}, options);
observer.observe(loadBtn);
})();
loadBtn.onclick = () => {
addNamesToList("ul", 10);
};
In addition to the IntersectionOberserver we have also added an onClick handler to the load more names button. As a fallback, if the IntersectionOberserver doesn't work then the user can also click on the button to load more names.
13