33
Implementing Pagination feature in Vanilla JS
Today we will implement a
Paginator
class which will have the following API :-// Initialization
const paginator = new Paginator(totalRecords,recordsPerPage,visiblePages);
// Usage
paginator.getActivePage();
paginator.gotoFirstPage();
paginator.gotoPrevPage();
paginator.gotoPage(page);
paginator.gotoNextPage();
paginator.gotoLastPage();
paginator.getVisiblePagesRange();
paginator.getActiveRecordsIndices();

The Class blueprint :-
class Paginator {
// Private class fields
#totalRecords;
#recordsPerPage;
#visiblePages;
#noOfPages;
#activePage;
#visiblePagesEndRange;
constructor(totalRecords, recordsPerPage, visiblePages) {
}
// Public class functions
getActivePage(){
}
gotoFirstPage() {
}
gotoPrevPage() {
}
gotoPage(page) {
}
gotoNextPage() {
}
gotoLastPage() {
}
getVisiblePagesRange() {
}
getActiveRecordsIndices() {
}
For all the explanations below, assume that
totalRecords
is 346, recordsPerPage
and visiblePages
are 6.
Let's start with the constructor :-
constructor(totalRecords, recordsPerPage, visiblePages) {
this.#recordsPerPage = recordsPerPage;
this.#totalRecords = totalRecords;
this.#noOfPages = Math.ceil(this.#totalRecords / this.#recordsPerPage);
this.#visiblePages = visiblePages;
this.#activePage = 1;
this.#visiblePagesEndRange = visiblePages;
}
#recordsPerPage
, #totalRecords
and #visiblePages
straight away get initialized to passed constructor parameters. #noOfPages
by dividing #totalRecords
by #recordsPerPage
. #activePage
as the name denotes is the page which will be active/selected in your pagination UI. It is initialized to 1. #visiblePagesEndRange
will be equivalent to #visiblePages
in the beginning and will help in maintaining a page range which comes into picture later on.getActivePage(){
return this.#activePage;
}
The above is a public function to return the private field
#activePage
. gotoFirstPage() {
this.#activePage = 1;
this.#visiblePagesEndRange = this.#visiblePages;
}
The above is a public function to set
#activePage
to 1 and #visiblePagesEndRange
to #visiblePages
(just like in constructor).gotoPrevPage() {
if (this.#activePage > 1) {
this.#activePage -= 1;
if (this.#activePage % this.#visiblePages === 0) {
this.#visiblePagesEndRange = this.#activePage;
}
}
}
The above is a public function which can used to decrement
#activePage
by 1 every time it is executed. Generally executed on a click of Prev button or a < UI icon. #activePage
can only be decremented if it is greater than 1. #activePage
is currently 7 and this function gets executed, #activePage
will change to 6 and it's modulus with #visiblePages
will be equivalent to 0. What this means is that the #activePage
now belongs to a lower visible page range and it's necessary to reflect that by updating #visiblePagesEndRange
by setting it equal to #activePage
itself.gotoPage(page) {
this.#activePage = page;
}
The above is a public function which is used to set
#activePage
to the passed page
parameter. gotoNextPage() {
if (this.#activePage < this.#noOfPages) {
this.#activePage += 1;
if (this.#activePage > this.#visiblePagesEndRange) {
this.#visiblePagesEndRange += this.#visiblePages;
this.#visiblePagesEndRange = Math.min(this.#visiblePagesEndRange, this.#noOfPages);
}
}
}
The above is a public function which can be used to increment
#activePage
by 1 every time it is executed. Generally executed on a click of Next button or a > UI icon. #activePage
can only be incremented if it is less than the #noOfPages
. #activePage
is currently 6 and this function gets executed, #activePage
will change to 7 but also go out of bounds of current #visiblePagesEndRange
so we will update that as well by an amount of #visiblePages
so that #visiblePagesEndRange
which was earlier 6 now becomes 12. #visiblePagesEndRange
cannot exceed the #noOfPages
and that's why if adding #visiblePages
to it results in an out of bounds, we take that into consideration by taking the minimum as shown above.gotoLastPage() {
this.#activePage = this.#noOfPages;
this.#visiblePagesEndRange = this.#noOfPages;
}
The above is a public function to set both
#activePage
and #visiblePagesEndRange
to #noOfPages
.getVisiblePagesRange() {
let beginningVisiblePage;
let endingVisiblePage;
if (this.#visiblePagesEndRange % this.#visiblePages === 0) {
beginningVisiblePage = this.#visiblePagesEndRange - this.#visiblePages + 1;
}
else {
beginningVisiblePage =
this.#visiblePagesEndRange - (this.#visiblePagesEndRange % this.#visiblePages) + 1;
}
endingVisiblePage = this.#visiblePagesEndRange;
return {
beginningVisiblePage,
endingVisiblePage
};
}
The above is a public function which is used to retrieve
beginningVisiblePage
and endingVisiblePage
by the means of which you can generate the respective UI page elements dynamically.For the beginningVisiblePage
:-
- If
#visiblePagesEndRange % this.#visiblePages
is 0, thenbeginningVisiblePage
can be set to#visiblePagesEndRange - this.#visiblePages + 1
- Otherwise, consider a scenario when the
#visiblePagesEndRange
will be 58 (this would happen in the last page range). Now 58 % 6 isn't 0 but 4. So we would need to subtract 4 from 58 and add 1 to it to get the correctbeginningVisiblePage
which will be 55. (Final page range is actually 55,56,57 and 58 for our current example).
The endingVisiblePage
will always be equal to #visiblePagesEndRange
.
getActiveRecordsIndices() {
let beginningRecordIndex = (this.#activePage - 1) * this.#recordsPerPage;
let endingRecordIndex = Math.min(
beginningRecordIndex + this.#recordsPerPage,
this.#totalRecords
);
return { beginningRecordIndex, endingRecordIndex };
}
}
The above is a public function which is used to retrieve
beginningRecordIndex
and endingRecordIndex
by the means of which you can generate the respective UI record elements dynamically. beginningRecordIndex
will be equal to #activePage-1
multiplied by the #recordsPerPage
. endingRecordIndex
will be minimum of beginningRecordIndex + #recordsPerPage
and #totalRecords
. Below is a codepen where you can see the
Paginator
class in action. Here there is an additional #validate
function which isn't important to basic implementation. And yes I haven't really applied the best CSS out there !!
I hope you enjoyed reading this piece :D. Also feel free to give any feedback. I just like to make something in vanilla JS every once in a while and not think too much about production readiness while making it. That's the part where you can come in and share your approaches.
33