Table of Contents
I wrote about client-side JavaScript pagination before, but I have never stopped searching for the perfect pagination method, both from a performance and a code size point of view. The idea pagination script should be small and load fast.
The script I coded is for modern browsers only, and it is fast enough to be included in the Thin UI library.
This is a JavaScript script that adds pagination functionality to HTML tables. Specifically, the script allows users to display only a certain number of rows per page – by dividing the table rows into pages – and navigate between pages using a set of page navigation links.
The demo
See the Pen Thin Table Pagination – JavaScript by Ciprian (@ciprian) on CodePen.
The code
Without further ado, ;) here is the JavaScript:
function addPagerToTables(tables, rowsPerPage = 10) {
// if the tables argument is a string, convert it to a NodeList of table elements
tables = typeof tables == "string" ? document.querySelectorAll(tables) : tables;
// loop through each table element and call the addPagerToTable function with the same rowsPerPage value
for (let table of tables) {
addPagerToTable(table, rowsPerPage);
}
}
function addPagerToTable(table, rowsPerPage = 10) {
// get all rows in the table body
let tBodyRows = table.querySelectorAll("tBody tr");
// calculate the total number of pages based on the number of rows and rowsPerPage
let numPages = Math.ceil(tBodyRows.length / rowsPerPage);
// get the total number of columns in the table
let colCount = [].slice
.call(table.querySelector("tr").cells)
.reduce((a, b) => a + parseInt(b.colSpan), 0);
// add a footer row to the table with a navigation div
table
.createTFoot()
.insertRow().innerHTML = `<td colspan=${colCount}><div class="nav"></div></td>`;
// if there's only one page, return (no need to add navigation links)
if (numPages == 1) {
return;
}
// generate the navigation links based on the number of pages
for (i = 0; i < numPages; i++) {
let pageNum = i + 1;
table
.querySelector(".nav")
.insertAdjacentHTML(
"beforeend",
`<a href="#" rel="${i}">${pageNum}</a> `
);
}
// display the first page of rows
changeToPage(table, 1, rowsPerPage);
// add a click event listener to each navigation link
for (let navA of table.querySelectorAll(".nav a")) {
navA.addEventListener("click", (e) => {
e.preventDefault();
// call the changeToPage function with the selected page number
changeToPage(table, parseInt(e.target.innerHTML), rowsPerPage);
});
}
}
function changeToPage(table, page, rowsPerPage) {
// calculate the start and end indexes of the rows to display
let startItem = (page - 1) * rowsPerPage;
let endItem = startItem + rowsPerPage;
// get all navigation links and table rows
let navAs = table.querySelectorAll(".nav a");
let tBodyRows = table.querySelectorAll("tbody tr");
// loop through each navigation link and table row
for (let nix = 0; nix < navAs.length; nix++) {
// if the current navigation link is the selected page, add the "active" class; otherwise, remove it
if (nix == page - 1) {
navAs[nix].classList.add("active");
} else {
navAs[nix].classList.remove("active");
}
// show or hide each table row based on whether it falls within the selected page's range
for (let trix = 0; trix < tBodyRows.length; trix++) {
tBodyRows[trix].style.display =
trix >= startItem && trix < endItem ? "table-row" : "none";
}
}
}
How it works
Here’s how it works. The script defines three functions:
addPagerToTables
– this function accepts two arguments:tables
, which can be either a string selector or an array of table elements, androwsPerPage
, which is the number of rows to display per page. It loops through each table element, calling theaddPagerToTable
function with the samerowsPerPage
value.addPagerToTable
– this function accepts two arguments:table
, which is the table element to which to add pagination, androwsPerPage
, which is the number of rows to display per page. It calculates the total number of pages based on the number of rows in the table, adds a footer row to the table with a navigation<div>
, and generates the page navigation links based on the number of pages. It also calls thechangeToPage
function to initially display the first page of rows, and adds a click event listener to each navigation link that calls thechangeToPage
function with the selected page number.changeToPage
– this function accepts three arguments:table
, which is the table element being paginated,page
, which is the page number to display, androwsPerPage
, which is the number of rows to display per page. It calculates the start and end indexes of the rows to display based on the selected page number and rows per page, adds or removes theactive
class from the selected navigation link, and shows or hides each row based on whether it falls within the selected page’s range.
The code compresses really well down to 964 bytes:
function addPagerToTables(e,t=10){e="string"==typeof e?document.querySelectorAll(e):e;for(let l of e)addPagerToTable(l,t)}function addPagerToTable(e,t=10){let l=e.querySelectorAll("tBody tr"),a=Math.ceil(l.length/t),r=[].slice.call(e.querySelector("tr").cells).reduce(((e,t)=>e+parseInt(t.colSpan)),0);if(e.createTFoot().insertRow().innerHTML=`<td colspan=${r}><div class="nav"></div></td>`,1!=a){for(i=0;i<a;i++){let t=i+1;e.querySelector(".nav").insertAdjacentHTML("beforeend",`<a href="#" rel="${i}">${t}</a> `)}changeToPage(e,1,t);for(let l of e.querySelectorAll(".nav a"))l.addEventListener("click",(l=>{l.preventDefault(),changeToPage(e,parseInt(l.target.innerHTML),t)}))}}function changeToPage(e,t,l){let a=(t-1)*l,r=a+l,o=e.querySelectorAll(".nav a"),n=e.querySelectorAll("tbody tr");for(let e=0;e<o.length;e++){e==t-1?o[e].classList.add("active"):o[e].classList.remove("active");for(let e=0;e<n.length;e++)n[e].style.display=e>=a&&e<r?"table-row":"none"}}
How to use
When you have your HTML table in place, with a correct structure (i.e. include <tbody>
and <tfoot>
), call it like this:
addPagerToTables('.my-table', 10);
Where 10
is the number of visible rows.
That’s it, it doesn’t even need CSS.