or send us a message

Magento 2 Ajax Product List

Written by: Simon Smith, On: Saturday 11th February 2017

We were recently asked to source and install an ajax infinite scroll module on a client's Magento2 site. An ajax list/scroll module allows the user to see the next pages of products without loading a whole new page. There are lots of modules out there for this purpose, but unfortunately most are flawed in one way or another.

The main issue with all the ones we auditioned - including one from a Magento Gold partner (not naming names), was on mobile/tablet. A serious issue given upto 2/3 of traffic is from such devices these days. The issue is when you click to a product and then press back, the product is either not showing, or the 'load more products' dialogue never loads as it was stopped when you clicked to the product.

So, we made our own script for this purpose, which I'd like to share with you. There are 3 steps to make this work on your theme;

Step 1 - Add product block identifier

Open your theme's list.phtml template, usually located in;


/app/design/frontend/VENTOR/THEME/Magento_Catalog/templates/product/list.phtml


Within this file, find the foreach loop which iterates over the products. If you are using grid and list view, you will have 2 of these in the file.


<?php foreach ($_productCollection as $_product): ?>
<?php /* @escapeNotVerified */ echo($iterator++ == 1) ? '<li class="item product product-item">' : '</li><li class="item product product-item">' ?>


In order for this to be able to return directly back to the product which was clicked when the user presses back, we need to add the product ID to the container block, like this;


<?php foreach ($_productCollection as $_product): ?>
<?php /* @escapeNotVerified */ echo($iterator++ == 1) ? '<li class="item product product-item" id="pmpid_'.$_product->getId().'">' : '</li><li class="item product product-item" id="pmpid_'.$_product->getId().'">' ?>


Step 2 - Add javascript

At the end of the same file (list.phtml) add the following;


<script type="text/javascript">
		require(['jquery'],function($){
		
			$jq = jQuery.noConflict();
			
			// Setup
			pagination = false;
			previouslink = false;
			nextlink = false;
			
			// Force go to product
			var hash = window.location.hash.substr(1);
			if(typeof hash != 'undefined' && hash.length) {
				$jq("html, body").scrollTop($jq("#"+hash).offset().top);
			}
			
			// Load next button if applicable
			var pagination = $jq(".pages").html();
		
			if(typeof pagination != 'undefined' && pagination.length) {
			 var nextlink = $jq(".pages-item-next a").attr("href");
			 	if(typeof nextlink != 'undefined' && nextlink.length) {
					var pnum = getParameterByName("p", nextlink)
					var nextHtml = '
LOAD MORE PRODUCTS
'; $jq(".products-grid").append(nextHtml); } } // Load previous button if applicable if(typeof pagination != 'undefined' && pagination.length) { var previouslink = $jq(".pages-item-previous a").attr("href"); if(typeof previouslink != 'undefined' && previouslink.length) { var pnum = getParameterByName("p", previouslink) var previousHtml = '
LOAD PREVIOUS PRODUCTS
'; $jq(".products-grid").prepend(previousHtml); } } // Load next function function loadNext() { var nextLink = $jq("body .next-load").data("next"); var pnum = $jq("body .next-load").data("pagenumber"); if(nextLink.length) { $jq.ajax({ url: nextLink, cache: false, success: function(response) { nextlinknext = false; pnumnext = false; items = $jq(response).find(".product-items"); $jq(response).find(".pages-item-next a").each(function() { if($jq(this).attr("href").length) { nextlinknext = $jq(this).attr("href"); pnumnext = getParameterByName("p", nextlinknext); } }); addItems(".pagenum_"+pnum,items,nextLink,"append"); $jq(".next-load").remove(); if(nextlinknext.length) { var nextHtml = '
LOAD MORE PRODUCTS
'; $jq(".products-grid").append(nextHtml); } } }); return true; } } function loadPrevious() { var nextLink = $jq("body .previous-load").data("next"); var pnum = $jq("body .previous-load").data("pagenumber"); if(nextLink.length) { $jq.ajax({ url: nextLink, cache: false, success: function(response) { nextlinknext = false; pnumnext = false; items = $jq(response).find(".product-items"); $jq(response).find(".pages-item-previous a").each(function() { if($jq(this).attr("href").length) { nextlinknext = $jq(this).attr("href"); pnumnext = getParameterByName("p", nextlinknext); } }); addItems(".pagenum_"+pnum,items,nextLink,'prepend'); $jq(".previous-load").remove(); if(nextlinknext.length) { var nextHtml = '
LOAD PREVIOUS PRODUCTS
'; $jq(".products-grid").prepend(nextHtml); } } }); return true; } } $jq("body").on("click",".next-load span",function() { $jq(this).parent().addClass("pm-ajax-running"); loadNext(); }); $jq("body").on("click",".previous-load span",function() { $jq(this).parent().addClass("pm-ajax-running"); loadPrevious(); }); $jq("body").on("click",".product-item a",function(e) { e.preventDefault(); returnUrl = false; var returnUrl = $jq(this).data("returnurl"); var pid = $jq(this).closest("li").attr("id"); var productUrl = $jq(this).attr("href"); if(returnUrl) { returnUrl = returnUrl.replace("html/?","html?"); } else { var href = window.location.href; if(href.indexOf("#") > 0){ href = href.split("#")[0]; returnUrl = href.replace("html/?","html?");; } else { returnUrl = href.replace("html/?","html?"); } } history.pushState('data', '', returnUrl+"#"+pid); window.location.href = productUrl; }); function addItems(target,html,returnUrl,dest) { $jq(target).html(html); $jq(target+" a").each(function() { $jq(this).attr("data-returnurl",returnUrl); }); newHtml = $jq(target+" ol").html(); if(dest == 'prepend') { $jq(".product-items").prepend(newHtml); } else { $jq(".product-items").append(newHtml); } $jq(target).remove(); } function getParameterByName(name, url) { if (!url) { url = window.location.href; } name = name.replace(/[\[\]]/g, "\\$&"); var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, " ")); } }); </script>

Step 3 - Add CSS/Images

Nice and simple this bit. Add/modify this css to suit your needs;


.page-products .pages { display:none !important; }
.pm-ajax-button { width:100%; text-align:center; margin:20px 0; }
.pm-ajax-button span { display:inline-block; padding:5px 10px;font-weight:normal;font-size:22px;margin:auto; background:#70938f; font-family:"Fjalla One",sans-serif; color:#fff; cursor:pointer; }
.pm-ajax-button span:hover { transition: background-color 300ms ease; background-color:#315550; }
/* MODIFY THE LINE BELOW TO SET YOUR ANIMATED 'LOADING' GIF */
.pm-ajax-running span { background:#315550 url(../images/ellipsis.gif) no-repeat center; width:100px; height:20px; padding:10px; transition:width 300ms ease; transition:padding 300ms ease; transition:height 300ms ease; font-size:0; display:inline-block; }


If you're looking for an animated loading gif, these guys are very good and free: http://loading.io/.

Enjoy.

Pixie Media

We are a Devon's first and only Magento Partner Agency. With over 18 years experience, we offer complete digital solutions to ecommerce clients across the globe.

Magento Business Partner
Pixie Media - Devon's first Magento Partner Agency
We Specialise In;