Infinite scroll
Bootstrap 5 Infinite scroll
This feature adds a scroll event listener (to the window or the component it's attached to if it has the overflow-y property set to scroll) and calls a callback method every time a user reaches an end of a page/container.
Note: Read the API tab to find all available options and advanced customization
Basic example
Scroll down the container below to add more items.
Note: Your element should be scrollable, for example, it should have
overflow-y: scroll
property like in the example below.
- Angry
- Dizzy
- Flushed
- Frown
- Grimace
- Grin
<div class="infinite-scroll" data-mdb-infinite-direction="y" data-mdb-infinite-container="infinite-scroll-basic">
<ul
class="container list-group infinite-scroll infinite-scroll-basic"
id="basic-example"
style="max-height: 261px; overflow-y: scroll"
>
<li class="list-group-item d-flex align-items-center">
<i class="far fa-angry fa-3x me-4"></i> Angry
</li>
<li class="list-group-item d-flex align-items-center">
<i class="far fa-dizzy fa-3x me-4"></i> Dizzy
</li>
<li class="list-group-item d-flex align-items-center">
<i class="far fa-flushed fa-3x me-4"></i> Flushed
</li>
<li class="list-group-item d-flex align-items-center">
<i class="far fa-frown fa-3x me-4"></i> Frown
</li>
<li class="list-group-item d-flex align-items-center">
<i class="far fa-grimace fa-3x me-4"></i> Grimace
</li>
<li class="list-group-item d-flex align-items-center">
<i class="far fa-grin fa-3x me-4"></i> Grin
</li>
</ul>
</div>
// An array of icon names
const icons = [
'Sad-Tear',
'Meh-Blank',
'Smile-Wink',
'Tired',
'Surprise',
'Kiss-Beam',
'Laugh-Squint',
];
// Get a scrollable container using an id attribute
const basicElement = document.getElementById('basic-example');
// An index of elements added after scrolling to the end of the container
let itemIndex = 0;
// items - an array of the created elements using the loop through icons array
const items = icons.map((icon) => {
// Create a list item element
const element = document.createElement('li');
// Change HTML code inside created list element using icon we are currently working on
element.innerHTML = `
<li class="list-group-item d-flex align-items-center">
<i class="far fa-${icon.toLowerCase()} fa-3x me-4"></i>${icon}
</li>
`;
// Return ready element
return element;
});
// Add an event listener to the scrollable container. The event below is triggered when a user scrolls to the end of the container
basicElement.addEventListener('complete.mdb.infiniteScroll', () => {
// Return nothing when user appended all of the generated items
if (itemIndex === icons.length - 1) return;
// Append next element to the scrollable container
basicElement.appendChild(items[itemIndex]);
// Increment amount of items that are appended
itemIndex++;
});
Direction
Use data-mdb-infinite-direction
to define the scrolling direction.
<div
class="infinite-scroll py-3 text-center"
id="direction-example"
style="max-width: 1500px; overflow-x: scroll; white-space: nowrap;"
data-mdb-infinite-direction="x"
>
<span class="mx-5"><i class="far fa-angry fa-3x me-4"></i> Angry</span>
<span class="mx-5"><i class="far fa-dizzy fa-3x me-4"></i> Dizzy</span>
<span class="mx-5"><i class="far fa-flushed fa-3x me-4"></i> Flushed</span>
<span class="mx-5"><i class="far fa-grimace fa-3x me-4"></i> Grimace</span>
<span class="mx-5"><i class="far fa-grin fa-3x me-4"></i> Grin</span>
</div>
// Get a scrollable container using an id attribute
const directionElement = document.getElementById('direction-example');
// An index of elements added after scrolling to the end of the container
let itemIndex = 0;
// An array of icon names
const icons = [
'Sad-Tear',
'Meh-Blank',
'Smile-Wink',
'Tired',
'Surprise',
'Kiss-Beam',
'Laugh-Squint',
];
// items - an array of the created elements using the loop through icons array
const items = icons.map((icon) => {
// Create a span element
const element = document.createElement('span');
// Add class mx-5 to the created element, which defines left and right margin
element.classList.add('mx-5');
// Change HTML code inside created span element using icon we are currently working on
element.innerHTML = `
<i class="far fa-${icon.toLowerCase()} fa-3x me-4"></i>
${icon}
`;
// Return ready element
return element;
});
// Add an event listener to the scrollable container. The event below is triggered when a user scrolls to the end of the container
directionElement.addEventListener('complete.mdb.infiniteScroll', () => {
// Return nothing when user appended all of the generated items
if (itemIndex === items.length - 1) return;
// Append next element to the scrollable container
directionElement.appendChild(items[itemIndex]);
// Increment amount of items that are appended
itemIndex++;
});
Spinners and asynchronous data
<div class="infinite-scroll py-3 text-center" id="spinners-and-async-example" style="max-height: 500px; overflow-y: scroll">
<div id="images">
<img src="https://mdbcdn.b-cdn.net/img/Photos/Slides/img%20(100).webp" class="img-fluid mb-3"/>
<img src="https://mdbcdn.b-cdn.net/img/Photos/Slides/img%20(105).webp" class="img-fluid mb-3"/>
<img src="https://mdbcdn.b-cdn.net/img/Photos/Slides/img%20(106).webp" class="img-fluid mb-3"/>
</div>
<div class="spinner-border mx-auto" id="spinner" style="display: none"></div>
</div>
// Get a spinner, container with images and scrollable container using an id attribute
const spinner = document.getElementById('spinner');
const imagesContainer = document.getElementById('images');
const infiniteContainer = document.getElementById('spinners-and-async-example');
// Function that generates image with data from API
const createImg = url => {
// Create an image element
let imgElement = document.createElement('img');
// Add .img-fluid class to the element, it will adjust size of it to the container
imgElement.classList.add('img-fluid');
// Set a src attribute using parameter that is passed to the function
imgElement.setAttribute('src', url);
// Return ready image element
return imgElement;
}
// Function that loads next image
const loadImages = () => {
// Make spinner visible
spinner.style.display = 'block';
// Fetch your API
fetch('YOUR_API/getNextItem')
.then(response => response.json)
.then(imgUrl => {
// Hide spinner after data loads
spinner.style.display = 'none';
// Append an image element generated by createImg function to the container with images
imagesContainer.appendChild(createImg(imgUrl));
});
}
// Add an event listener to the scrollable container. The event below is triggered when a user scrolls to the end of the container
infiniteContainer.addEventListener('complete.mdb.infiniteScroll', loadImages);
Window
You can apply the mdb.InfiniteScroll
instance to a window.
Note: You have to initialize an instance on your own, using JavaScript. If
you are using other containers, you have to make a check if your event.target
is
a window
.
<!--Main layout-->
<main class="my-4">
<div class="container">
<!--Section: Posts-->
<section class="text-center mb-4" id="posts">
<div class="row">
<div class="col-md-6 mb-4">
<div class="bg-image hover-overlay ripple shadow-1-strong rounded mb-4" data-mdb-ripple-color="light">
<img
data-mdb-lazy-src="https://mdbcdn.b-cdn.net/img/Photos/Others/images/29.webp"
data-mdb-lazy-placeholder="https://placehold.it/1321x583?text=Loading"
class="w-100 lazy"
/>
<a href="#!">
<div class="mask" style="background-color: rgba(251, 251, 251, 0.2);"></div>
</a>
</div>
<h5>This is an title of the article</h5>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam cupiditate veniam voluptatibus laudantium cum dolorem
illo. Quos architecto deserunt saepe.
</p>
<a class="btn btn-info btn-rounded" href="#!" role="button">Read more</a>
</div>
<div class="col-md-6 mb-4">
<div class="bg-image hover-overlay ripple shadow-1-strong rounded mb-4" data-mdb-ripple-color="light">
<img
data-mdb-lazy-src="https://mdbcdn.b-cdn.net/img/Photos/Others/images/27.webp"
data-mdb-lazy-placeholder="https://placehold.it/1321x583?text=Loading"
class="w-100 lazy"
/>
<a href="#!">
<div class="mask" style="background-color: rgba(251, 251, 251, 0.2);"></div>
</a>
</div>
<h5>This is an title of the article</h5>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam cupiditate veniam voluptatibus laudantium cum dolorem
illo. Quos architecto deserunt saepe.
</p>
<a class="btn btn-info btn-rounded" href="#!" role="button">Read more</a>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4">
<div class="bg-image hover-overlay ripple shadow-1-strong rounded mb-4" data-mdb-ripple-color="light">
<img
data-mdb-lazy-src="https://mdbcdn.b-cdn.net/img/Photos/Others/images/25.webp"
data-mdb-lazy-placeholder="https://placehold.it/1321x583?text=Loading"
class="w-100 lazy"
/>
<a href="#!">
<div class="mask" style="background-color: rgba(251, 251, 251, 0.2);"></div>
</a>
</div>
<h5>This is an title of the article</h5>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam cupiditate veniam voluptatibus laudantium cum dolorem
illo. Quos architecto deserunt saepe.
</p>
<a class="btn btn-info btn-rounded" href="#!" role="button">Read more</a>
</div>
<div class="col-md-6 mb-4">
<div class="bg-image hover-overlay ripple shadow-1-strong rounded mb-4" data-mdb-ripple-color="light">
<img
data-mdb-lazy-src="https://mdbcdn.b-cdn.net/img/Photos/Others/images/24.webp"
data-mdb-lazy-placeholder="https://placehold.it/1321x583?text=Loading"
class="w-100 lazy"
/>
<a href="#!">
<div class="mask" style="background-color: rgba(251, 251, 251, 0.2);"></div>
</a>
</div>
<h5>This is an title of the article</h5>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam cupiditate veniam voluptatibus laudantium cum dolorem
illo. Quos architecto deserunt saepe.
</p>
<a class="btn btn-info btn-rounded" href="#!" role="button">Read more</a>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4">
<div class="bg-image hover-overlay ripple shadow-1-strong rounded mb-4" data-mdb-ripple-color="light">
<img
data-mdb-lazy-src="https://mdbcdn.b-cdn.net/img/Photos/Others/images/31.webp"
data-mdb-lazy-placeholder="https://placehold.it/1321x583?text=Loading"
class="w-100 lazy"
/>
<a href="#!">
<div class="mask" style="background-color: rgba(251, 251, 251, 0.2);"></div>
</a>
</div>
<h5>This is an title of the article</h5>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam cupiditate veniam voluptatibus laudantium cum dolorem
illo. Quos architecto deserunt saepe.
</p>
<a class="btn btn-info btn-rounded" href="#!" role="button">Read more</a>
</div>
<div class="col-md-6 mb-4">
<div class="bg-image hover-overlay ripple shadow-1-strong rounded mb-4" data-mdb-ripple-color="light">
<img
data-mdb-lazy-src="https://mdbcdn.b-cdn.net/img/Photos/Others/images/23.webp"
data-mdb-lazy-placeholder="https://placehold.it/1321x583?text=Loading"
class="w-100 lazy"
/>
<a href="#!">
<div class="mask" style="background-color: rgba(251, 251, 251, 0.2);"></div>
</a>
</div>
<h5>This is an title of the article</h5>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam cupiditate veniam voluptatibus laudantium cum dolorem
illo. Quos architecto deserunt saepe.
</p>
<a class="btn btn-info btn-rounded" href="#!" role="button">Read more</a>
</div>
</div>
<div class="row" id="spinner" style="display: none;">
<div class="col-md-12">
<div class="spinner-border mx-auto"></div>
</div>
</div>
</section>
<!--Section: Posts-->
</div>
</main>
<!--Main layout-->
const postsContainer = document.getElementById('posts');
const spinner = document.getElementById('spinner');
const items = [
{
img: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/31.webp',
title: 'This is an title of the article',
text: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe.',
},
{
img: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/23.webp',
title: 'This is an title of the article',
text: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe.',
},
{
img: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/29.webp',
title: 'This is an title of the article',
text: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe.',
},
{
img: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/27.webp',
title: 'This is an title of the article',
text: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe.',
},
{
img: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/25.webp',
title: 'This is an title of the article',
text: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe.',
},
{
img: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/24.webp',
title: 'This is an title of the article',
text: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe.',
},
{
img: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/31.webp',
title: 'This is an title of the article',
text: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe.',
},
{
img: 'https://mdbcdn.b-cdn.net/img/Photos/Others/images/32.webp',
title: 'This is an title of the article',
text: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cupiditate veniam voluptatibus laudantium cum dolorem illo. Quos architecto deserunt saepe.',
},
];
const getPostTemplate = (post) => {
// returns the HTML template with post's title, image & text
return `
<div class="col-md-6 mb-4">
<div class="bg-image hover-overlay ripple shadow-1-strong rounded mb-4" data-mdb-ripple-color="light">
<img src="${post.img}" class="w-100 lazy"/>
<a href="#!">
<div class="mask" style="background-color: rgba(251, 251, 251, 0.2);">
</div>
</a>
</div>
<h5>${post.title}</h5>
<p>${post.text}</p>
<a class="btn btn-info btn-rounded" href="#!" role="button">Read more</a>
</div>
`;
};
// posts - array of templates
const posts = items.map((item) => getPostTemplate(item));
const generateRow = (firstPost, secondPost) => {
// Returns a div.row element with two columns generated based on arguments
let el = document.createElement('div');
el.classList.add('row');
el.innerHTML = `
${firstPost}
${secondPost}
`;
return el;
};
// rows - array of rows with two posts each
const rows = [];
// iterates over posts and creates a row for every two of them
for (let i = 0; i < posts.length - 1; i += 2) {
rows.push(generateRow(posts[i], posts[i + 1]));
}
// renderedItems - number of items already rendered
let renderedItems = 0;
const renderItems = (index) => {
// timeout simulates delay in loading items (f.e. API call)
setTimeout(() => {
// hide spinner
spinner.style.display = 'none';
postsContainer.appendChild(rows[index]);
}, 1500);
};
const loadItems = () => {
if (renderedItems < rows.length) {
// show spinner
postsContainer.appendChild(spinner);
spinner.style.display = 'flex';
renderItems(renderedItems);
renderedItems++;
// Removes event listener after all items have been loaded
if (renderedItems === rows.length) {
window.removeEventListener('complete.mdb.infiniteScroll', loadItems);
}
}
};
// load items when window is scrolled to the end
window.addEventListener('complete.mdb.infiniteScroll', loadItems);
// init infinite scroll
new mdb.InfiniteScroll(window);
Infinite scroll - API
Usage
Via data attributes
<div class="infinite-scroll" data-mdb-infinite-direction="..." data-mdb-infinite-container="...">
Sample content
</div>
Via JavaScript
const infiniteScrollInstance = new mdb.InfiniteScroll(document.getElementById('element'), {
infinite-direction: '...',
});
Via jQuery
Note: By default, MDB does not include jQuery and you have to add it to the project on your own.
$(document).ready(() => {
$('.example-class').infiniteScroll(options);
});
Options
Options can be passed via data attributes, JavaScript, or jQuery. For data attributes, append
the option name to data-mdb-
, as in data-mdb-infinite-direction=""
.
Name | Type | Default | Description |
---|---|---|---|
infinite-direction
|
String | 'y' |
Defines an example scroll direction. |
Methods
Name | Description | Example |
---|---|---|
dispose
|
Removes an instance of the lazy element |
infiniteScrollInstance.toggle()
|
getInstance
|
Static method which allows you to get the infinite scroll instance associated to a DOM element. |
InfiniteScroll.getInstance(infiniteScrollElement)
|
getOrCreateInstance
|
Static method which returns the infinite scroll instance associated to a DOM element or create a new one in case it wasn't initialized. |
InfiniteScroll.getOrCreateInstance(infiniteScrollElement)
|
const infiniteScrollElement = document.getElementById('element');
const infiniteScrollInstance = new mdb.InfiniteScroll(infiniteScrollElement);
infiniteScrollInstance.dispose();
Events
Name | Description |
---|---|
complete.mdb.infiniteScroll
|
This event fires immediately after scrolling to the end of the container. |
const infiniteScrollElement = document.getElementById('element');
infiniteScrollElement.addEventListener('complete.mdb.infiniteScroll', (e) => {
// do something...
});
Import
MDB UI KIT also works with module bundlers. Use the following code to import this component:
import { InfiniteScroll } from 'mdb-ui-kit';