In woocommerce archive / category and taxonomy pages I create an infinit scroll for products. this is my code so far:
functions.php
add_filter('woocommerce_product_loop_start', 'add_data_page_to_products_list');
function add_data_page_to_products_list($output){
// Use regex to match the opening <ul> tag and add the data-page="1" attribute
$output = preg_replace('/<ul class="products([^"]*)">/', '<ul class="products$1" data-page="1">', $output);
return $output;
}
/******************** Remove Pagination & add load more button insted of it ********************/
remove_action('woocommerce_after_shop_loop', 'woocommerce_pagination', 10);
// Add a Load More button after the product loop
add_action('woocommerce_after_shop_loop', 'custom_load_more_button', 15);
function custom_load_more_button(){
global $wp_query;
$total_products = $wp_query->found_posts; // Get the total number of products available
echo '<div id="loader" class="loader-styles" data-page="1" data-total-product-number="' . $total_products . '" style="display: none; text-align: center;">';
echo '<div class="loader-styles__a"></div>';
echo '<div class="loader-styles__b"></div>';
echo '<div class="loader-styles__c"></div>';
echo '</div>';
}
// Pass posts_per_page and ajax URL to JavaScript
add_action('wp_enqueue_scripts', 'enqueue_infinit_products_scripts');
function enqueue_infinit_products_scripts() {
if (is_shop() || is_product_category() || is_product_tag()) {
wp_enqueue_script('infinit-products', get_template_directory_uri() . '/assets/scripts/product-infinite-scroll.js', [], null, true);
// Get the posts_per_page setting from WordPress
// $posts_per_page = get_option('posts_per_page');
// Pass ajax URL and posts_per_page to JavaScript
wp_localize_script('infinit-products', 'ajax_params', array(
'ajax_url' => admin_url('admin-ajax.php'),
// 'posts_per_page' => $posts_per_page, // Passing the setting to JS
));
}
}
js:
document.addEventListener("DOMContentLoaded", function() {
/******************* Ajax Infinite Scroll with Loader *******************/
if (document.querySelector(".products")) {
let canLoad = true;
let page = 1;
const productWrapper = document.querySelector(".products");
const loader = document.getElementById("loader");
const baseUrl = window.location.href.split("page/")[0];
const totalProducts = parseInt(loader.getAttribute('data-total-product-number'), 10); // Get total number of products
const initialPage = parseInt(window.location.href.split("page/")[1]) || 1; // Get the page number from URL, default to 1
// Function to load products up to a specific page
function loadProductsUpToPage(targetPage) {
let loadCompleted = 0;
function loadNextPage() {
if (loadCompleted >= targetPage) return; // Stop if we've loaded up to the target page
loadCompleted++; // Increment first to keep track of the correct page
const xhr = new XMLHttpRequest();
xhr.open("GET", `${baseUrl}page/${loadCompleted}/`, true);
xhr.onload = function() {
if (xhr.status === 200) {
const parser = new DOMParser();
const doc = parser.parseFromString(xhr.responseText, "text/html");
const newProducts = doc.querySelectorAll(".products .product");
newProducts.forEach((product) => {
productWrapper.appendChild(product);
});
if (loadCompleted < targetPage) {
loadNextPage();
} else {
canLoad = true;
updateUrl(targetPage);
if (productWrapper.querySelectorAll(".product").length >= totalProducts) {
loader.style.display = "none"; // Hide loader if all products are loaded
}
}
}
};
xhr.send();
}
loadNextPage();
}
// Initial load if coming directly to a specific page
if (initialPage > 1) {
canLoad = false;
loadProductsUpToPage(initialPage);
}
function loadMoreProducts() {
if (!canLoad) return;
canLoad = false;
loader.style.display = "flex"; // Show the loader
const xhr = new XMLHttpRequest();
xhr.open("GET", `${baseUrl}page/${page + 1}/`, true);
xhr.onload = function() {
if (xhr.status === 200) {
const parser = new DOMParser();
const doc = parser.parseFromString(xhr.responseText, "text/html");
const newProducts = doc.querySelectorAll(".products .product");
if (newProducts.length) {
newProducts.forEach((product) => {
productWrapper.appendChild(product);
});
page++;
canLoad = true;
loader.style.display = "none"; // Hide loader
updateUrl(page);
if (productWrapper.querySelectorAll(".product").length >= totalProducts) {
loader.style.display = "none"; // Hide loader if all products are loaded
}
} else {
loader.style.display = "none"; // Hide loader if no more products
}
}
};
xhr.send();
}
// Update the URL with the current page
function updateUrl(pageNumber) {
const newUrl = pageNumber > 1 ? `${baseUrl}page/${pageNumber}/` : baseUrl;
window.history.replaceState({
page: pageNumber
}, "", newUrl);
}
// Function to check if the user has reached the bottom of the product list
function checkScrollPosition() {
const scrollPosition = window.scrollY + window.innerHeight;
const productListBottom = productWrapper.getBoundingClientRect().bottom + window.scrollY;
// Trigger loading when the user reaches the bottom of the products list
if (scrollPosition >= productListBottom && canLoad) {
loadMoreProducts();
}
}
// Trigger product loading when the user scrolls
window.addEventListener("scroll", function() {
checkScrollPosition();
});
// Update the current page in the URL based on scroll position
function determineCurrentPage() {
const products = document.querySelectorAll(".products .product");
let visiblePage = page;
products.forEach((product, index) => {
const rect = product.getBoundingClientRect();
if (rect.top < window.innerHeight && rect.bottom > 0) {
visiblePage = Math.ceil((index + 1) / 20); // Adjust this divisor according to how many products are on each page
}
});
if (visiblePage !== page) {
page = visiblePage;
updateUrl(page);
}
}
// Update page URL based on visible product list during scrolling
window.addEventListener("scroll", function() {
determineCurrentPage();
});
}
});
css:
.loader-styles {
width: 100%;
height: 6em;
margin: 1.5em;
justify-content: center
}
.loader-styles__a, .loader-styles__b, .loader-styles__c {
border-radius: 50%;
width: 1em;
height: 1em;
transform-origin: 50% 100%;
animation: bounce1 1s linear infinite
}
.loader-styles__a {
background: var(--primary-color)
}
.loader-styles__b {
background: var(--secondary-color);
animation-delay: .1s
}
.loader-styles__c {
background: #255ff4;
animation-delay: .2s
}
now a feature that I want to add is for example when scroll down to page 3 and click on a product and go to it’s page then want to back and see prev page, it should load all products from page one to 3 and stand on the product that I clicked. now it just show page 3 products with url of page 3 and when I scroll down to the loader, it shows page 1 products and url changed to /page/2/ and so on. can anyone write a code for that part?