Skip to content

Instantly share code, notes, and snippets.

@kimili
Created September 24, 2019 18:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kimili/e689154d742745e16a181eb557ae69dd to your computer and use it in GitHub Desktop.
Save kimili/e689154d742745e16a181eb557ae69dd to your computer and use it in GitHub Desktop.
Instagram-style lazy page loading, triggered when a user scrolls towards the bottom of the list. Imports Axios for XHR calls, and ImagesLoaded for handling updates when the images have finished loading. The templating is done in Twig, in the context of a CraftCMS site.
{% set image = snapshot.asset.one() %}
<article class="snapshot snapshot--card">
<header class="snapshot__header">
<h3 class="snapshot__title visually-hidden">
<a href="{{ snapshot.url }}" data-target="overlay" data-overlay-full-width="true">{{ snapshot.title }}</a>
</h3>
<p class="snapshot__author">{{ snapshot.author }}</p>
<p class="snapshot__location">{{ snapshot.geography.one() }}</p>
</header>
<picture class="snapshot__image">
<source
media="(max-width: 767px)"
srcset="{{ image.getUrl('snapshotFeed_sm') }} 370w,
{{ image.getUrl('snapshotFeed_md') }} 740w,
{{ image.getUrl('snapshotFeed_lg') }} 1440w">
<source
media="(min-width: 768px)"
srcset="{{ image.getUrl('snapshotFeedCropped_2x') }},
{{ image.getUrl('snapshotFeedCropped_2x') }} 2x">
<img
src="{{ image.getUrl('thumbnailLarge_2x') }}"
alt="{{ snapshot.title }}" />
</picture>
<div class="snapshot__text">
<p class="snapshot__caption">
{{ snapshot.caption | chop(unit='w', limit=16) | typogrify }}
</p>
<div class="snapshot__actions">
<div id="snapshot_{{ snapshot.id }}__like">
{% include "_like-form.twig" %}
</div>
</div>
</div>
</article>
<div class="snapshots-feed__cards" data-pagination-on-scroll="true">
<div class="snapshots-feed__page" data-page="{{ pageInfo.currentPage }}" {% if pageInfo.nextUrl %}data-next-page-url="{{ pageInfo.nextUrl }}"{% endif %}>
{% for snapshot in pageEntries %}
{% include "_snapshot.twig" %}
{% endfor %}
</div>
</div>
import Axios from "axios";
import imagesloaded from "imagesloaded";
class InfiniteScrollPagination {
constructor(
containerSelector = "[data-pagination-on-scroll]",
threshold = [0, 0.2, 0.4, 0.6, 0.8, 1.0]
) {
this.container = document.querySelector(containerSelector);
if (!this.container) {
return;
}
this.threshold = threshold;
this.observer = new IntersectionObserver(this.listener.bind(this), {
threshold
});
this.updateListener();
}
set(page) {
this.pageElement = page;
imagesloaded(this.pageElement, () => {
this.observer.observe(this.pageElement);
});
}
unset() {
if (!this.pageElement) {
return;
}
this.observer.unobserve(this.pageElement);
this.pageElement = null;
}
listener(entries) {
const isIntersecting = !!entries
.map(entry => entry.isIntersecting)
.filter(Boolean).length;
if (!isIntersecting) {
return;
}
this.container.classList.add("loading");
this.unset();
this.loadNextPage().then(() => {
this.container.classList.remove("loading");
this.updateListener();
});
}
loadNextPage() {
this.lastLoadedPage = this.getLastLoadedPage();
const request = Axios.request({
url: this.lastLoadedPage.dataset.nextPageUrl,
method: "get",
headers: {
"X-Requested-With": "XMLHttpRequest"
}
})
.then(response => {
this.container.insertAdjacentHTML("beforeend", response.data);
})
.catch(function(error) {
console.error(error);
});
return request;
}
updateListener() {
this.lastLoadedPage = this.getLastLoadedPage();
if (this.hasMorePages()) {
this.set(this.lastLoadedPage);
}
}
getLastLoadedPage() {
const pages = this.container.querySelectorAll("[data-page]");
if (!pages.length) {
return null;
}
return pages[pages.length - 1];
}
hasMorePages() {
if (!this.lastLoadedPage) {
return false;
}
return !!this.lastLoadedPage.dataset.nextPageUrl;
}
}
export default InfiniteScrollPagination;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment