Skip to content

Instantly share code, notes, and snippets.

@hpawe01
Last active March 10, 2023 06:07
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hpawe01/da787b5d98661b1e323c7d50654a06e5 to your computer and use it in GitHub Desktop.
Save hpawe01/da787b5d98661b1e323c7d50654a06e5 to your computer and use it in GitHub Desktop.
Shopify Dawn Infinite Scroll for Collections (modern plain javascript)
class InfiniteScroll extends HTMLElement {
constructor() {
super();
this.productsContainerSelector = this.getAttribute('data-products-container');
this.paginationSelector = this.getAttribute('data-pagination-selector');
this.nextPageSelector = this.getAttribute('data-next-page-selector');
this.productsContainerEl = this.querySelector(this.productsContainerSelector);
this.paginationEl = this.querySelector(this.paginationSelector);
this.loadingIndicator = this.querySelector('.loading-overlay-wrapper');
if (!this.productsContainerEl || !this.paginationEl) {
console.error('Wrong configuration for infinite scroll');
return;
}
try {
this.observer = this.registerObserver(this.loadNextPage.bind(this));
this.observer.observe(this.paginationEl);
this.paginationEl.style.visibility = 'hidden';
} catch (error) {}
}
registerObserver(callback) {
let callbackIsRunning = false
return new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting && !callbackIsRunning) {
callbackIsRunning = true;
callback()
.then(_ => callbackIsRunning = false)
.catch(_ => callbackIsRunning = false);
}
})
}
loadNextPage() {
const nextPageEl = this.paginationEl.querySelector(this.nextPageSelector);
if (!nextPageEl) {
this.observer.disconnect();
return Promise.resolve();
}
const nextPageUrl = nextPageEl.getAttribute('href');
this.loadingIndicator.classList.remove('hidden');
return fetch(nextPageUrl)
.then(result => {
this.loadingIndicator.classList.add('hidden')
if (!result.ok) {
throw(result.statusText);
}
return result.text()
})
.then(this.handleNextPage.bind(this))
}
handleNextPage(nextPageContent) {
const nextPageHtml = new DOMParser().parseFromString(nextPageContent, 'text/html')
const nextPageProducts = nextPageHtml.querySelector(this.productsContainerSelector)
if (!nextPageProducts) {
console.warn('Infinite scroll: Could not find any products in next page', nextPageHtml);
return;
}
this.productsContainerEl.insertAdjacentHTML('beforeend', nextPageProducts.innerHTML);
const nextPagePagination = nextPageHtml.querySelector(this.paginationSelector);
if (!nextPagePagination) {
console.warn('Infinite scroll: Could not find any pagination in next page');
return;
}
this.paginationEl.innerHTML = nextPagePagination.innerHTML;
}
}
customElements.define('infinite-scroll', InfiniteScroll);
<!-- ... -->
<script src="{{ 'infinite-scroll.js' | asset_url }}" defer="defer"></script>
<style>
.loading-overlay-wrapper {
text-align: center;
margin-top: 5rem;
}
</style>
<infinite-scroll data-products-container="#product-grid-wrapper" data-pagination-selector=".pagination" data-next-page-selector=".pagination__item--next">
<div id="product-grid-wrapper">
<ul id="product-grid" data-id="{{ section.id }}" class="
grid grid--2-col negative-margin product-grid
{% if collection.products_count < 4 %} grid--{{ collection.products_count }}-col-tablet{% else %}
{% if collection.products_count == 4 %} grid--4-col-desktop{% else %} grid--3-col-tablet grid--one-third-max grid--4-col-desktop grid--quarter-max{% endif %}
{% endif %}">
{%- for product in collection.products -%}
<li class="grid__item grid__item--product">
{% render 'product-card',
product_card_product: product,
media_size: section.settings.image_ratio,
show_secondary_image: section.settings.show_secondary_image,
add_image_padding: section.settings.add_image_padding,
show_vendor: section.settings.show_vendor,
show_image_outline: section.settings.show_image_outline,
show_rating: section.settings.show_rating
%}
</li>
{%- endfor -%}
</ul>
</div>
<!-- optional loading element -->
<div class="loading-overlay-wrapper hidden">
<div class="loading-overlay__spinner">
<svg aria-hidden="true" focusable="false" role="presentation" class="spinner" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<circle class="path" fill="none" stroke-width="6" cx="33" cy="33" r="30"></circle>
</svg>
</div>
</div>
{%- if paginate.pages > 1 -%}
{% render 'pagination', paginate: paginate, anchor: '' %}
{%- endif -%}
</infinite-scroll>
<!-- ... -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment