Use Javascript to Build a HTML Pagination

Demo: https://adriancs.com/html-css-js/1372/demo-of-using-javascript-to-build-html-pagination/

Source Code: https://adriancs.com/wp-content/uploads/2023/11/demo-pagination-javascript-2.zip

Assume that we have the following HTML element:

<div id="pagination1" class="pager1"></div>

The following javascript function is generated by ChatGPT 4.0. By following some of my requirements, this javascript function will help to generate a HTML pagination block with the following parameters:

  • totalRecords and totalRecordsPerPage will be used for calculating the page numbers.
  • totalPagerSlots will define the total pagination buttons.
  • pageFileldName and container used for fillling in the page numbers.
  • hrefURL and onclickScript will be used as a basic string format for generating the links.

Here’s the finalized working code:

function generatePagination(totalRecords, totalRecordsPerPage,
    currentPage, totalPagerSlots, pageFieldName,
    hrefURL, onclickScript) {

    // Calculate the total number of pages
    let totalPages = Math.ceil(totalRecords / totalRecordsPerPage);

    let pagerHTML = "";

    // Determine how many page links to show on either side of the current page
    let halfPagerSlots = Math.floor(totalPagerSlots / 2);

    // Calculate the first page number to display in the pager
    let pagerStart = Math.max(currentPage - halfPagerSlots, 1);

    // Calculate the last page number to display in the pager
    let pagerEnd = Math.min(pagerStart + totalPagerSlots - 1, totalPages);

    // Adjust the pager start if we're at the end of the page range
    if (pagerEnd - pagerStart < totalPagerSlots - 1) {
        pagerStart = Math.max(pagerEnd - totalPagerSlots + 1, 1);
    }

    // If not on the first pager slot, add a link to the first page
    if (pagerStart > 1) {
        let hrefFirstPage = hrefURL.replace(pageFieldName, '1');
        let firstPageOnClick = onclickScript ? onclickScript.replace(pageFieldName, '1') : '';
        pagerHTML += `<a href="${hrefFirstPage}"${firstPageOnClick ? ` onclick="${firstPageOnClick}"` : ''}>First</a>\n`;

        // Add ellipsis to indicate skipped pages
        pagerHTML += "<span class='ellipsis'>...</span>\n";
    }

    // Loop through page numbers from the start to end of the pager
    for (let i = pagerStart; i <= pagerEnd; i++) {

        // Replace the pageFieldName placeholder with the actual page number
        let hrefValue = hrefURL.replace(pageFieldName, i);

        // Begin anchor tag for the page link
        let linkHTML = `<a href="${hrefValue}"`;

        // If an onclick script is provided, add it to the anchor tag
        if (onclickScript) {
            let onClickValue = onclickScript.replace(pageFieldName, i);
            linkHTML += ` onclick="${onClickValue}"`;
        }

        // Finish the anchor tag and add the page number as the link text
        linkHTML += `>${i}</a>\n`;

        // If this is the current page, wrap it in a <strong> tag to highlight it
        if (i === currentPage) {
            pagerHTML += `<strong>${linkHTML}</strong>\n`;
        } else {
            // Otherwise, add the link to the pager HTML as is
            pagerHTML += linkHTML;
        }
    }

    // If not on the last pager slot, add a link to the last page
    if (pagerEnd < totalPages) {

        // Add ellipsis to indicate skipped pages
        pagerHTML += "<span class='ellipsis'>...</span>\n";

        let hrefLastPage = hrefURL.replace(pageFieldName, totalPages);
        let lastPageOnClick = onclickScript ? onclickScript.replace(pageFieldName, totalPages) : '';
        pagerHTML += `<a href="${hrefLastPage}"${lastPageOnClick ? ` onclick="${lastPageOnClick}"` : ''}>Last</a>`;
    }

    // Return the complete HTML for the pager
    return pagerHTML;
}

This function requires two dynamic values.

  • Total records (or toal rows in database perpective)
  • Current page number

To get number of total records, in SQL database perspective, one would normally do this:

SELECT COUNT(*) FROM tablename where ...<conditions>....

For the number of current page, if you are doing non-javascript direct link page navigation, it can be obtained from the URL’s query string or route parameter (route data).

A javascript to get the page number from URL’s query string:

// Assuming the URL is something like: https://www.example.com/?page=2
let queryString = window.location.search;
let urlParams = new URLSearchParams(queryString);
let page = urlParams.get("page");

Example of Usage

Example of usage 1: Direct link only, no javascript onclick event

let htmlPagination = generatePagination(10000, 10, 9, 7, '[page]',
                     'SearchItems?page=[page]', '');

document.getElementById("pagination1").innerHTML = htmlPagination;

Result:

<a href="SearchItems?page=1">First</a>
<span class='ellipsis'>...</span>
<a href="SearchItems?page=6">6</a>
<a href="SearchItems?page=7">7</a>
<a href="SearchItems?page=8">8</a>
<strong><a href="SearchItems?page=9">9</a>
</strong>
<a href="SearchItems?page=10">10</a>
<a href="SearchItems?page=11">11</a>
<a href="SearchItems?page=12">12</a>
<span class='ellipsis'>...</span>
<a href="SearchItems?page=1000">Last</a>

Example of usage 2: No redirect link, only javascript onclick events

let htmlPagination = generatePagination(10000, 10, 9, 7, '[page]', '', 
                    'return loadTable([page], event);');

document.getElementById("pagination1").innerHTML = htmlPagination;

Result:

<a href="" onclick="return loadTable(1, event);">First</a>
<span class='ellipsis'>...</span>
<a href="" onclick="return loadTable(6, event);">6</a>
<a href="" onclick="return loadTable(7, event);">7</a>
<a href="" onclick="return loadTable(8, event);">8</a>
<strong><a href="" onclick="return loadTable(9, event);">9</a>
</strong>
<a href="" onclick="return loadTable(10, event);">10</a>
<a href="" onclick="return loadTable(11, event);">11</a>
<a href="" onclick="return loadTable(12, event);">12</a>
<span class='ellipsis'>...</span>
<a href="" onclick="return loadTable(1000, event);">Last</a>

Example of usage 3: Combo, Direct Link + Javascript onclick event

let htmlPagination = generatePagination(10000, 10, 9, 7, '[page]',
                'SearchItems?page=[page]', 'return loadTable([page], event);');

document.getElementById("pagination1").innerHTML = htmlPagination;

Result:

<a href="SearchItems?page=1" onclick="return loadTable(1, event);">First</a>
<span class='ellipsis'>...</span>
<a href="SearchItems?page=6" onclick="return loadTable(6, event);">6</a>
<a href="SearchItems?page=7" onclick="return loadTable(7, event);">7</a>
<a href="SearchItems?page=8" onclick="return loadTable(8, event);">8</a>
<strong><a href="SearchItems?page=9" onclick="return loadTable(9, event);">9</a>
</strong>
<a href="SearchItems?page=10" onclick="return loadTable(10, event);">10</a>
<a href="SearchItems?page=11" onclick="return loadTable(11, event);">11</a>
<a href="SearchItems?page=12" onclick="return loadTable(12, event);">12</a>
<span class='ellipsis'>...</span>
<a href="SearchItems?page=1000" onclick="return loadTable(1000, event);">Last</a>

The onclick javascript event provides an override mechanism which makes the pagination system serve a dual purpose. They contain an href attribute for standard navigation, ensuring that the links remain functional and accessible without JavaScript. However, they also include an onclick event handler that overrides the default behavior when a user performs a left-click. On a left-click, rather than navigating to a new page, the loadTable function is invoked, which leverages FetchAPI to update the table content dynamically on the same page. This approach provides a seamless user experience by avoiding full page reloads, thereby enhancing the responsiveness and interactivity of the web application.

The following code demonstrates the idea of overriding the default direct-link behaviour that will use FetchAPI to load the table:

function loadTable(pageNumber, event) {

    // Check if the click is a standard left-click
    if (!event || event.button === 0) {

        // Prevent default link behavior on left click
        event.preventDefault();

        // ... additional code to load the table ...
        // ... using FetchApi, etc...

        return false;
    }
    
    // For non-left clicks, allow the default behavior (href navigation)
    return true;
}

Example with FetchAPI:

function loadTable(pageNumber, event) {

    // Check if the click is a standard left-click
    if (!event || event.button === 0) {

        // Prevent default link behavior on left click
        event.preventDefault();

        // Fetch data from server
        fetch('https://your-api-endpoint.com/data?page=' + pageNumber)
            .then(response => {
            
                // Check if the response is ok (status code 200-299)
                if (!response.ok) {
                    throw new Error('Network response was not ok ' + response.statusText);
                }
                return response.json(); // Parse JSON response
            })
            .then(data => {
            
                // Build HTML table
                let table = '<table>';
                table += '<tr>';
                
                // Add headers to table, assuming 'headers' is an array of header names
                data.headers.forEach(header => {
                    table += '<th>' + header + '</th>';
                });
                table += '</tr>';

                // Add rows to table, assuming 'rows' is an array of row objects
                data.rows.forEach(row => {
                    table += '<tr>';
                    Object.values(row).forEach(value => {
                        table += '<td>' + value + '</td>';
                    });
                    table += '</tr>';
                });
                table += '</table>';

                // Add the table to the page, assuming you have a div with id 'tableContainer'
                document.getElementById('tableContainer').innerHTML = table;
            })
            .catch(error => {
                console.error('There has been a problem with your fetch operation:', error);
            });

        return false;
    }

    // For non-left clicks, allow the default behavior (href navigation)
    return true;
}

Example of usage 4: Use pure javascript in HREF

let htmlPagination = generatePagination(10000, 10, 9, 7, '[page]',
                    'javascript:loadTable([page])', '');

Result:

<a href="javascript:loadTable(1)">First</a>
<span class='ellipsis'>...</span>
<a href="javascript:loadTable(6)">6</a>
<a href="javascript:loadTable(7)">7</a>
<a href="javascript:loadTable(8)">8</a>
<strong><a href="javascript:loadTable(9)">9</a>
</strong>
<a href="javascript:loadTable(10)">10</a>
<a href="javascript:loadTable(11)">11</a>
<a href="javascript:loadTable(12)">12</a>
<span class='ellipsis'>...</span>
<a href="javascript:loadTable(1000)">Last</a>

CSS Styling for The Pagination

Here’s the basic style adopted by the following examples.

body {
    font-family: Arial;
    font-size: 12pt;
}

CSS Example 1:

.pager1 a {
    border-radius: 7px;
    background-color: lightblue;
    text-decoration: none; 
    padding: 7px 10px;
    color: black;
    font-style: normal; 
}

.pager1 a:hover, .pager1 strong a {
    background-color: darkblue;
    color: white;
}

CSS Example 2:

.pager1 a {
    border-radius: 7px;
    background-color: lightblue;
    text-decoration: none;
    padding: 7px 10px;
    color: black;
    font-style: normal;
}

.pager1 a:hover, .pager1 strong a {
    background-color: darkblue;
    color: white;
}

.pager1 {
    color: white;
}

.pager1 {
  padding: 10px;
  background-color: #333; 
  border-radius: 5px; 
}

.pager1 a {
  text-decoration: none;
  margin: 0 5px; 
  padding: 5px 10px;
  border-radius: 5px;
  background-color: #555;
  color: #ddd; 
  transition: background-color 0.3s, color 0.3s; 
}

.pager1 a:hover,
.pager1 a:focus {
  background-color: #777; 
  color: #fff; 
  outline: none;
}

.pager1 strong a {
  background-color: #0088cc; 
  color: #fff;
  pointer-events: none; 
  cursor: default; 
}

.pager1 .ellipsis {
  color: #aaa; 
  pointer-events: none;
}

CSS Example 3:

.pager1 {
  text-align: center; 
  padding: 10px; 
  background-color: #2f2f2f; 
  border-radius: 5px; 
  color: #b9e986;
}

.pager1 a {
  text-decoration: none; 
  margin: 0 5px;
  padding: 5px 10px;
  border-radius: 5px;
  background-color: #3e3e3e; 
  color: #b9e986; 
  transition: background-color 0.3s, color 0.3s; 
}

.pager1 a:hover,
.pager1 a:focus {
  background-color: #4e4e4e; 
  color: #d4fcb8; 
  outline: none; 
}

.pager1 strong a {
  background-color: #8dc641; 
  color: #2f2f2f; 
  pointer-events: none;
  cursor: default; 
}

.pager1 .ellipsis {
  color: #6f6f6f; 
  pointer-events: none; 
}

CSS Example 4:

.pager1 {
  text-align: center;
  padding: 10px; 
  background-color: #fff0f0; 
  border-radius: 5px; 
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 
}

.pager1 a {
  text-decoration: none; 
  margin: 0 5px; 
  padding: 5px 10px;
  border-radius: 5px;
  background-color: #ffe5e5; 
  color: #333; 
  transition: background-color 0.3s, color 0.3s;
}

.pager1 a:hover,
.pager1 a:focus {
  background-color: #ffc3c3; 
  color: #d60000; 
  outline: none; 
}

.pager1 strong a {
  background-color: #ff9999; 
  color: #fff; 
  pointer-events: none; 
  cursor: default; 
}

.pager1 .ellipsis {
  color: #bfbfbf; 
  pointer-events: none; 
}

CSS Example 5:

.pager1 {
  text-align: center;
  padding: 10px;
  background-color: #f0faff;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.pager1 a {
  text-decoration: none;
  margin: 0 5px; 
  padding: 5px 10px; 
  border-radius: 5px;
  background-color: #e1efff;
  color: #007bff; 
  transition: background-color 0.3s, color 0.3s; 
}

.pager1 a:hover,
.pager1 a:focus {
  background-color: #d0e7ff; 
  color: #0056b3; 
  outline: none; 
}

.pager1 strong a {
  background-color: #007bff; 
  color: #ffffff; 
  pointer-events: none; 
  cursor: default; 
}

.pager1 .ellipsis {
  color: #bfbfbf; 
  pointer-events: none; 
}