Day 21: Pagination + Load More Button
Fetch a dataset from local JSON, render a limited set of items, and build both pagination and Load More controls to manage how users move through the data.
JavaScript focus
- fetching local JSON
- storing dataset state
- slicing arrays
- tracking page or visible count
- replacing vs appending DOM content
- enabling and disabling controls
- re-rendering UI based on state
Nice extras
- include Prev / Next buttons for pagination
- show the current page number
- show how many items are visible out of the total
- hide the Load More button once all items are visible
- keep pagination and load more as separate demos on the same page
- add a loading state while fetching
- add an empty state if the dataset is empty
- keep render logic separate from control logic
MDN prep
Pagination + Load More Button
Load More
Pagination
The HTML
<section class="section pagination_load-more">
<h2 class="secondary_heading">Pagination + Load More Button</h2>
<!-- For demo purposes only, hide the container not in use. -->
<div class="load_more" hidden>
<h3 class="tertiary_heading">Load More</h3>
<div class="load_more-items"></div>
<div class="button_container">
<button type="button" class="button load_more-button">Load More</button>
</div>
</div>
<!-- For demo purposes only, hide the container not in use. -->
<div class="pagination">
<h3 class="tertiary_heading">Pagination</h3>
<div class="pagination_items"></div>
<div class="button_container">
<button type="button" class="button prev_button">Previous</button>
<button type="button" class="button next_button">Next</button>
</div>
</div>
</section>
The JavaScript
async function fetchLoadPaginationData() {
try {
const response = await fetch("../data/load-more-pagination.json");
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error("There was a problem: ", error);
}
}
let page = 0;
const itemsPerPage = 6;
function countAndSliceData(data, page) {
let start = page * itemsPerPage;
let end = start + itemsPerPage;
let visible = data.slice(start, end);
return visible;
}
function renderUI(data) {
const loadMore = document.querySelector(".load_more");
const pagination = document.querySelector(".pagination");
if (!loadMore || !pagination) return;
const loadMoreItems = loadMore.querySelector(".load_more-items");
const paginationItems = pagination.querySelector(".pagination_items");
paginationItems.textContent = "";
const visible = countAndSliceData(data, page);
visible.forEach((item) => {
const article = document.createElement("article");
article.classList.add("article");
const figure = document.createElement("figure");
figure.classList.add("figure");
const img = document.createElement("img");
const h4 = document.createElement("h4");
const desc = document.createElement("p");
const link = document.createElement("a");
h4.textContent = `${item.id}. ${item.title}`;
img.src = item.image;
img.alt = item.title;
desc.textContent = item.description;
link.href = item.url;
link.textContent = "Read more... ";
figure.append(img);
article.append(figure, h4, desc, link);
/*
* comment out either load more or pagination
* they don't work at the same time
*/
// loadMoreItems.append(article);
paginationItems.append(article);
});
}
async function init() {
const data = await fetchLoadPaginationData();
if (!data) return;
const prev = document.querySelector(".prev_button");
prev.disabled = true;
const next = document.querySelector(".next_button");
next.disabled = false;
const load = document.querySelector(".load_more-button");
load.disabled = false;
const totalPages = Math.ceil(data.length / itemsPerPage);
console.log({ totalPages });
console.log({ page });
prev.addEventListener("click", () => {
if (page > 0) {
page--;
console.log({ page });
renderUI(data);
next.disabled = false;
}
if (page === 0) {
prev.disabled = true;
}
});
next.addEventListener("click", () => {
if (page < totalPages - 1) {
page++;
console.log({ page });
renderUI(data);
prev.disabled = false;
}
if (page === totalPages - 1) {
next.disabled = true;
}
});
load.addEventListener("click", () => {
if (page < totalPages - 1) {
page++;
console.log({ page });
renderUI(data);
}
if (page === totalPages - 1) {
load.disabled = true;
}
});
renderUI(data);
}
init();