Skip to content

Instantly share code, notes, and snippets.

@webuilder240
Created July 7, 2024 13:57
Show Gist options
  • Select an option

  • Save webuilder240/5993e7f01f941dc5a3d5c5471bd69669 to your computer and use it in GitHub Desktop.

Select an option

Save webuilder240/5993e7f01f941dc5a3d5c5471bd69669 to your computer and use it in GitHub Desktop.
export async function requestGet(url) {
const response = await fetch(url)
return await response.text()
}
export function nextFrame() {
return new Promise(requestAnimationFrame)
}
export function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
<p style="color: green"><%= notice %></p>
<h1>Users</h1>
<infinite-scroll-pagination data-manual-load="false" data-root-margin="40px">
<% @users.each do |user| %>
<div>
<%= render user %>
<p>
<%= link_to "Show this user", user %>
</p>
</div>
<% end %>
<%= link_to("relead more", users_path(page: @users.next_page), class: "pagination-link", data: { preload: @users.first_page? }) unless @users.last_page? %>
</infinite-scroll-pagination>
<%= link_to "New user", new_user_path %>
import { delay, nextFrame, requestGet } from "../helper";
class InfiniteScrollPagination extends HTMLElement {
constructor() {
super();
this.nextPageLink = null;
this.manualLoad = this.getAttribute('data-manual-load') === 'true';
this.rootMargin = this.getAttribute('data-root-margin') || '0px';
}
connectedCallback() {
this.observeNextPageLink();
}
async loadNextPage(event) {
event?.preventDefault();
this.setAttribute("aria-busy", "true");
const html = await this.loadNextPageHTML();
await nextFrame();
this.setAttribute("aria-busy", "false");
this.nextPageLink.outerHTML = html;
await delay(500);
this.observeNextPageLink();
}
async observeNextPageLink() {
if (this.manualLoad) return;
await nextFrame();
const nextPageLink = this.querySelector('.pagination-link');
if (!nextPageLink) return;
this.nextPageLink = nextPageLink;
if (nextPageLink.dataset.preload === "true") {
this.loadNextPage();
} else {
await this.nextIntersection(nextPageLink, this.intersectionOptions);
this.loadNextPage();
}
}
async loadNextPageHTML() {
const html = await requestGet(this.nextPageLink.href);
const doc = new DOMParser().parseFromString(html, "text/html");
const element = doc.querySelector('infinite-scroll-pagination');
return element ? element.innerHTML.trim() : "";
}
get intersectionOptions() {
const root = this.scrollableOffsetParent;
return {
root: root,
rootMargin: this.rootMargin
};
}
get scrollableOffsetParent() {
const root = this.offsetParent;
return root && root.scrollHeight > root.clientHeight ? root : null;
}
nextIntersection(element, options = {}) {
return new Promise(resolve => {
new IntersectionObserver(([entry], observer) => {
if (!entry.isIntersecting) return;
observer.disconnect();
resolve();
}, options).observe(element);
});
}
}
customElements.define('infinite-scroll-pagination', InfiniteScrollPagination);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment