Created
April 27, 2025 15:27
-
-
Save caendesilva/774885d50424b424edd9cd7b7200ce5b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* HydePHP Posts Paginator | |
* A client-side pagination solution for HydePHP blog post listings | |
* | |
* Simply add this script to your posts page and configure the options below. | |
*/ | |
(function() { | |
// Configuration options | |
const config = { | |
postsPerPage: 5, // Number of posts to show per page | |
containerSelector: '.max-w-3xl.mx-auto', // Container holding all posts | |
postSelector: 'article', // Selector for individual post elements | |
showPageNumbers: true, // Whether to show numbered page buttons | |
maxPageButtons: 5, // Max number of page number buttons to display | |
scrollToTop: true, // Whether to scroll to top on page change | |
scrollOffset: 100, // Offset from top when scrolling (in pixels) | |
texts: { // Customizable text labels | |
prev: 'Previous', | |
next: 'Next', | |
page: 'Page' | |
}, | |
classes: { // Customizable CSS classes | |
paginationContainer: 'flex items-center justify-center space-x-2 mt-8 mb-12', | |
pageButton: 'px-3 py-1 rounded-md transition-colors duration-200', | |
activePageButton: 'bg-indigo-500 text-white', | |
inactivePageButton: 'bg-gray-200 text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600', | |
prevNextButton: 'px-4 py-1 rounded-md bg-gray-200 hover:bg-gray-300 text-gray-700 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600 transition-colors duration-200', | |
disabledButton: 'opacity-50 cursor-not-allowed' | |
} | |
}; | |
// Wait for the DOM to be fully loaded | |
document.addEventListener('DOMContentLoaded', function() { | |
// Find the container element | |
const container = document.querySelector(config.containerSelector); | |
if (!container) return; | |
// Find all posts | |
const posts = Array.from(container.querySelectorAll(config.postSelector)); | |
if (!posts.length) return; | |
// Calculate the total number of pages | |
const totalPages = Math.ceil(posts.length / config.postsPerPage); | |
if (totalPages <= 1) return; // No need for pagination if there's only one page | |
// Create pagination controls container | |
const paginationContainer = document.createElement('div'); | |
paginationContainer.className = config.classes.paginationContainer; | |
paginationContainer.setAttribute('role', 'navigation'); | |
paginationContainer.setAttribute('aria-label', 'Pagination'); | |
// Create state for current page | |
let currentPage = 1; | |
// Function to update the visible posts | |
function updateVisiblePosts() { | |
// Calculate indexes | |
const startIndex = (currentPage - 1) * config.postsPerPage; | |
const endIndex = Math.min(startIndex + config.postsPerPage, posts.length); | |
// Hide all posts | |
posts.forEach(post => post.style.display = 'none'); | |
// Show only the posts for the current page | |
for (let i = startIndex; i < endIndex; i++) { | |
posts[i].style.display = 'block'; | |
} | |
// Update pagination controls | |
updatePaginationControls(); | |
// Scroll to top if configured | |
if (config.scrollToTop) { | |
window.scrollTo({ | |
top: container.offsetTop - config.scrollOffset, | |
behavior: 'smooth' | |
}); | |
} | |
} | |
// Function to update pagination controls | |
function updatePaginationControls() { | |
// Clear previous controls | |
paginationContainer.innerHTML = ''; | |
// Add previous button | |
const prevButton = document.createElement('button'); | |
prevButton.textContent = config.texts.prev; | |
prevButton.className = config.classes.prevNextButton; | |
prevButton.setAttribute('aria-label', 'Go to previous page'); | |
if (currentPage === 1) { | |
prevButton.disabled = true; | |
prevButton.className += ' ' + config.classes.disabledButton; | |
} else { | |
prevButton.addEventListener('click', () => { | |
currentPage--; | |
updateVisiblePosts(); | |
}); | |
} | |
paginationContainer.appendChild(prevButton); | |
// Add page number buttons if configured | |
if (config.showPageNumbers) { | |
// Determine which page numbers to show | |
let startPage = Math.max(1, currentPage - Math.floor(config.maxPageButtons / 2)); | |
let endPage = Math.min(totalPages, startPage + config.maxPageButtons - 1); | |
// Adjust if we're near the end | |
if (endPage - startPage + 1 < config.maxPageButtons) { | |
startPage = Math.max(1, endPage - config.maxPageButtons + 1); | |
} | |
// Add ellipsis at start if needed | |
if (startPage > 1) { | |
const ellipsis = document.createElement('span'); | |
ellipsis.textContent = '...'; | |
ellipsis.className = 'px-2'; | |
paginationContainer.appendChild(ellipsis); | |
} | |
// Add page buttons | |
for (let i = startPage; i <= endPage; i++) { | |
const pageButton = document.createElement('button'); | |
pageButton.textContent = i; | |
pageButton.className = config.classes.pageButton + ' ' + | |
(i === currentPage ? config.classes.activePageButton : config.classes.inactivePageButton); | |
pageButton.setAttribute('aria-label', `${config.texts.page} ${i}`); | |
pageButton.setAttribute('aria-current', i === currentPage ? 'page' : 'false'); | |
if (i !== currentPage) { | |
pageButton.addEventListener('click', () => { | |
currentPage = i; | |
updateVisiblePosts(); | |
}); | |
} | |
paginationContainer.appendChild(pageButton); | |
} | |
// Add ellipsis at end if needed | |
if (endPage < totalPages) { | |
const ellipsis = document.createElement('span'); | |
ellipsis.textContent = '...'; | |
ellipsis.className = 'px-2'; | |
paginationContainer.appendChild(ellipsis); | |
} | |
} | |
// Add next button | |
const nextButton = document.createElement('button'); | |
nextButton.textContent = config.texts.next; | |
nextButton.className = config.classes.prevNextButton; | |
nextButton.setAttribute('aria-label', 'Go to next page'); | |
if (currentPage === totalPages) { | |
nextButton.disabled = true; | |
nextButton.className += ' ' + config.classes.disabledButton; | |
} else { | |
nextButton.addEventListener('click', () => { | |
currentPage++; | |
updateVisiblePosts(); | |
}); | |
} | |
paginationContainer.appendChild(nextButton); | |
} | |
// Initialize pagination | |
container.appendChild(paginationContainer); | |
updateVisiblePosts(); | |
// Add a post counter | |
const counter = document.createElement('div'); | |
counter.className = 'text-sm text-center text-gray-600 dark:text-gray-400 mt-2'; | |
counter.textContent = `Showing page ${currentPage} of ${totalPages} (${posts.length} total posts)`; | |
paginationContainer.after(counter); | |
// Update counter when page changes | |
const updateCounter = () => { | |
counter.textContent = `Showing page ${currentPage} of ${totalPages} (${posts.length} total posts)`; | |
}; | |
// Add event listeners to update counter | |
paginationContainer.addEventListener('click', updateCounter); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment