How to Implement Pagination with Vanilla JavaScript
When you’re displaying a lot of information on a web page, it’s helpful to split it up into different sections. So, in this tutorial, we’ll implement fully functional pagination on a web page using JavaScript.
Pagination is used to break up a block of content into navigable pages on a webpage.
Aside from making it easier to see information, it also helps when loading data from a server. Using pagination, you can decide to only load a fixed number of items each time when the user decides to see them. This helps save time and avoid information overload on a page.
Here’s a sneak peak at the final product:
Our pagination element will use both page numbers and next page & previous page buttons for navigation.
Let’s get started!
1. Place the Content With HTML
In this demo, we’ll be placing the content to be paginated using HTML. In a real life example, we might be getting our content as JSON from a server so we’ll also take a look at how the pagination logic can be applied to this when we’re writing our JavaScript code.
First, we define our paginated content as a list:
1 |
<ul id="paginated-list" aria-live="polite"> |
2 |
<li>Item 1</li> |
3 |
<li>Item 2</li> |
4 |
<li>Item 3</li> |
5 |
... |
6 |
</ul>
|
aria-live
property so assistive technologies know when the content in this region has been updated and announce the new content to the reader. Next we define a nav
element that contains next and previous buttons and the pagination numbers container:
1 |
<nav class="pagination-container"> |
2 |
<button class="pagination-button" id="prev-button" title="Previous page" aria-label="Previous page"> |
3 |
<
|
4 |
</button>
|
5 |
|
6 |
<div id="pagination-numbers"> |
7 |
|
8 |
</div>
|
9 |
|
10 |
<button class="pagination-button" id="next-button" title="Next page" aria-label="Next page"> |
11 |
>
|
12 |
</button>
|
13 |
</nav>
|
The pagination-numbers div
is empty for now as the page numbers will be determined using JavaScript.
2. Styling with CSS
We’re taking a fairly minimalistic approach for this demo so we’ll only style the pagination numbers and the container.
Place the pagination-container at the bottom of the page, override the default button styling for the pagination number and set an active
class to show which page number is currently selected:
1 |
.pagination-container { |
2 |
display: flex; |
3 |
align-items: center; |
4 |
position: absolute; |
5 |
bottom: 0; |
6 |
justify-content: center; |
7 |
}
|
8 |
|
9 |
.pagination-number, |
10 |
.pagination-button{ |
11 |
font-size: 1.1rem; |
12 |
background-color: transparent; |
13 |
border: none; |
14 |
margin: 0.25rem 0.25rem; |
15 |
cursor: pointer; |
16 |
height: 2.5rem; |
17 |
width: 2.5rem; |
18 |
border-radius: .2rem; |
19 |
}
|
20 |
|
21 |
.pagination-number:hover, |
22 |
.pagination-button:not(.disabled):hover { |
23 |
background: #fff; |
24 |
}
|
25 |
|
26 |
.pagination-number.active { |
27 |
color: #fff; |
28 |
background: #0085b6; |
29 |
}
|
We’ve also added some simple :hover
styling, but use the :not()
selector to make sure it doesn’t apply to the disabled buttons.
3. Pagination Logic with JavaScript
Now to make it work. Let’s define what we’re trying to achieve with pagination:
- Only display a specific number of items on each page
- Display page numbers based on how many times the total items are broken down
- When a page number is clicked, change the display to that page
- Allow navigating to previous and next pages
First, get all the elements we’ll need:
1 |
const paginationNumbers = document.getElementById("pagination-numbers"); |
2 |
const paginatedList = document.getElementById("paginated-list"); |
3 |
const listItems = paginatedList.querySelectorAll("li"); |
4 |
const nextButton = document.getElementById("next-button"); |
5 |
const prevButton = document.getElementById("prev-button"); |
Next, we’ll define our global variables:
-
paginationLimit
: how many items we want displayed on each page; and -
pageCount
: how many pages there will be based on the paginationLimit. -
currentPage
: store the value of the currentPage
Calculate the pageCount
by dividing the total number of items (listItems.length
) by the paginationLimit
and rounding to the highest whole number using the Math.ceil
function.
“The
Math.ceil()
function always rounds a number up to the next largest integer.” - MDN
So if we have 50 items and we only want to display 10 items per page, our page count will be 50/10 = 5 pages. Likewise, if we have 55 items and we want to display 10 items per page, our page count will be 55/10 = 5.5 which rounds up to 6 pages.
1 |
const paginationLimit = 10; |
2 |
const pageCount = Math.ceil(listItems.length / paginationLimit); |
3 |
let currentPage; |
Add Page Numbers
Now that we know how many pages we’ll need, we can define a function to create a new button for the page number and then add the buttons to the paginationNumbers
container.
1 |
const appendPageNumber = (index) => { |
2 |
const pageNumber = document.createElement("button"); |
3 |
pageNumber.className = "pagination-number"; |
4 |
pageNumber.innerHTML = index; |
5 |
pageNumber.setAttribute("page-index", index); |
6 |
pageNumber.setAttribute("aria-label", "Page " + index); |
7 |
|
8 |
paginationNumbers.appendChild(pageNumber); |
9 |
};
|
10 |
|
11 |
const getPaginationNumbers = () => { |
12 |
for (let i = 1; i <= pageCount; i++) { |
13 |
appendPageNumber(i); |
14 |
}
|
15 |
};
|
And then we’ll call the getPaginationNumbers
function when the web page loads using the window.load()
event:
1 |
window.addEventListener("load", () => { |
2 |
getPaginationNumbers(); |
3 |
});
|
Display Active Page
We want to define a function to only display as many items are allowed in the paginationLimit
on each page. Here’s how we’ll implement it:
Set the value of the currentPage
variable to the pageNum
value:
1 |
const setCurrentPage = (pageNum) => { |
2 |
currentPage = pageNum; |
3 |
};
|
Get the range for items to be shown. If we’re on page 1, we want to show items 1 to 10 (according to the paginationLimit
). if we’re on page 2, we want to show items 11 to 20 and so on.
1 |
const setCurrentPage = (pageNum) => { |
2 |
currentPage = pageNum; |
3 |
|
4 |
const prevRange = (pageNum - 1) * paginationLimit; |
5 |
const currRange = pageNum * paginationLimit; |
6 |
};
|
Loop through the list of items to be displayed and hide all the items. Then unhide all the items that fall within the range.
Note: since we’re working with arrays, item 1 will be at 0 and item 10 will be at position 9 so our logic looks like this:
1 |
const setCurrentPage = (pageNum) => { |
2 |
currentPage = pageNum; |
3 |
|
4 |
const prevRange = (pageNum - 1) * paginationLimit; |
5 |
const currRange = pageNum * paginationLimit; |
6 |
|
7 |
listItems.forEach((item, index) => { |
8 |
item.classList.add("hidden"); |
9 |
if (index >= prevRange && index < currRange) { |
10 |
item.classList.remove("hidden"); |
11 |
}
|
12 |
});
|
13 |
};
|
We use this method since our implementation is just hiding HTML content. If we were trying to append content from a JSON object to the DOM, we could update the logic to look something like:
1 |
jsonData.forEach((item, index) => { |
2 |
elementContainer.innerHTML = '' |
3 |
if (index >= prevRange && index < currRange) { |
4 |
elementContainer.appendChild(item) |
5 |
}
|
6 |
});
|
Update the window.load()
event to set the current page as page 1 once the webpage loads:
1 |
window.addEventListener("load", () => { |
2 |
getPaginationNumbers(); |
3 |
setCurrentPage(1); |
4 |
});
|
Add Page Number Buttons Event Listener
Use a click event listener to trigger the setCurrentPage
function whenever a page number button is clicked. Place this function inside the window.load()
event so now the function looks like this:
1 |
window.addEventListener("load", () => { |
2 |
getPaginationNumbers(); |
3 |
setCurrentPage(1); |
4 |
|
5 |
document.querySelectorAll(".pagination-number").forEach((button) => { |
6 |
const pageIndex = Number(button.getAttribute("page-index")); |
7 |
|
8 |
if (pageIndex) { |
9 |
button.addEventListener("click", () => { |
10 |
setCurrentPage(pageIndex); |
11 |
});
|
12 |
}
|
13 |
});
|
14 |
});
|
Set Active Page Number
We also want to define a function to add the active class to the page number we just clicked. We reset all buttons on the page by removing the active class. Then, if the page-index of the button matches the currentPage
global variable, we add the active class to that button.
1 |
const handleActivePageNumber = () => { |
2 |
document.querySelectorAll(".pagination-number").forEach((button) => { |
3 |
button.classList.remove("active"); |
4 |
|
5 |
const pageIndex = Number(button.getAttribute("page-index")); |
6 |
if (pageIndex == currentPage) { |
7 |
button.classList.add("active"); |
8 |
}
|
9 |
});
|
10 |
};
|
Include this function in the setCurrentPage
function so the active page number is updated every time a new page is set:
1 |
const setCurrentPage = (pageNum) => { |
2 |
currentPage = pageNum; |
3 |
|
4 |
handleActivePageNumber(); |
5 |
|
6 |
const prevRange = (pageNum - 1) * paginationLimit; |
7 |
const currRange = pageNum * paginationLimit; |
8 |
|
9 |
listItems.forEach((item, index) => { |
10 |
item.classList.add("hidden"); |
11 |
if (index >= prevRange && index < currRange) { |
12 |
item.classList.remove("hidden"); |
13 |
}
|
14 |
});
|
15 |
};
|
At this point, we should have a functional Page navigation system that looks like this:
Next and Previous Buttons
Let’s extend this functionality to include the next and previous buttons. We can use the same implementation in the setCurrentPage()
function, and update the logic:
- for the previous button, we change the page using
setCurrentPage(currentPage - 1)
- for the next button, we change the page using
setCurrentPage(currentPage + 1)
Include the event listeners for next and previous buttons in window.load()
:
1 |
window.addEventListener("load", () => { |
2 |
getPaginationNumbers(); |
3 |
setCurrentPage(1); |
4 |
|
5 |
prevButton.addEventListener("click", () => { |
6 |
setCurrentPage(currentPage - 1); |
7 |
});
|
8 |
|
9 |
nextButton.addEventListener("click", () => { |
10 |
setCurrentPage(currentPage + 1); |
11 |
});
|
12 |
|
13 |
document.querySelectorAll(".pagination-number").forEach((button) => { |
14 |
const pageIndex = Number(button.getAttribute("page-index")); |
15 |
|
16 |
if (pageIndex) { |
17 |
button.addEventListener("click", () => { |
18 |
setCurrentPage(pageIndex); |
19 |
});
|
20 |
}
|
21 |
});
|
22 |
});
|
Disable Page Navigation Buttons
We also want to include a disabling feature on the next and previous buttons.
- disable the previous button if we’re on the first page (if current page is 1)
- disable the next button if we're on the last page (if current page is total number of pages)
1 |
const disableButton = (button) => { |
2 |
button.classList.add("disabled"); |
3 |
button.setAttribute("disabled", true); |
4 |
};
|
5 |
|
6 |
const enableButton = (button) => { |
7 |
button.classList.remove("disabled"); |
8 |
button.removeAttribute("disabled"); |
9 |
};
|
10 |
|
11 |
const handlePageButtonsStatus = () => { |
12 |
if (currentPage === 1) { |
13 |
disableButton(prevButton); |
14 |
} else { |
15 |
enableButton(prevButton); |
16 |
}
|
17 |
|
18 |
if (pageCount === currentPage) { |
19 |
disableButton(nextButton); |
20 |
} else { |
21 |
enableButton(nextButton); |
22 |
}
|
23 |
};
|
Finally, pass the handlePageButtonsStatus()
function into the setCurrentPage()
function so we check the navigation every time a new page is clicked:
1 |
const setCurrentPage = (pageNum) => { |
2 |
currentPage = pageNum; |
3 |
|
4 |
handleActivePageNumber(); |
5 |
handlePageButtonsStatus(); |
6 |
|
7 |
const prevRange = (pageNum - 1) * paginationLimit; |
8 |
const currRange = pageNum * paginationLimit; |
9 |
|
10 |
listItems.forEach((item, index) => { |
11 |
item.classList.add("hidden"); |
12 |
if (index >= prevRange && index < currRange) { |
13 |
item.classList.remove("hidden"); |
14 |
}
|
15 |
});
|
16 |
};
|
Conclusion
And that’s all folks! We now have a fully functional and accessible implementation of a paginated section.
Level up Your Front End JavaScript Skills!
We have loads of great foundational and practical JavaScript tutorials on Tuts+, so you can learn as you create:
- How To Build a Simple Carousel With Vanilla JavaScript (14 Lines of Code!)Jemima Abu02 Jun 2022
- What is the DOM API (and How is it Used to Write JavaScript for the Web)?Anna Monus01 Dec 2021
- How to Create a Sticky Toolbar With CSS and a Bit of JavaScriptGeorge Martsoukos19 Apr 2022
- How to Implement Smooth Scrolling With CSS & JavaScriptGeorge Martsoukos04 Apr 2023
- How to Generate Random Background Colors With JavaScriptJemima Abu31 May 2021
- Building a Vertical Timeline With CSS and a Touch of JavaScriptGeorge Martsoukos06 May 2021
- Build a Simple Weather App With Vanilla JavaScriptGeorge Martsoukos30 Apr 2021