I have a 4 list of posts. I am trying to load more posts (clones of the "original" ones) when I reach the bottom of the container.
class InfiniteScroll {
constructor() {
this.observer = null;
this.isLoading = false;
this.postsContainer = document.querySelector('#postsContainer');
this.postsArary = postsContainer.querySelectorAll('.post');
this.iterationCount = Number(this.postsContainer.dataset.currentPage);
this.perPage = 5;
this.maxCount = this.postsContainer?.dataset?.maxCount;
this.numberOfPages = Math.ceil(this.maxCount / this.perPage);
this.hasNextPage = this.iterationCount < this.numberOfPages;
}
loadMorePosts() {
if (this.hasNextPage) {
this.postsArary.forEach(item => {
let postClone = item.cloneNode(true);
this.postsContainer.appendChild(postClone);
});
} else {
if (this.observer) this.observer.disconnect();
}
}
getLastPost() {
const allPosts = postsContainer.querySelectorAll('.post');
return allPosts[allPosts.length - 1];
}
counter() {
if (this.hasNextPage) {
this.iterationCount = this.iterationCount + 1;
this.postsContainer.dataset.currentPage = this.iterationCount;
}
this.postsContainer.dataset.hasNextPage = this.hasNextPage;
this.hasNextPage = this.iterationCount < this.numberOfPages - 1;
}
bindLoadMoreObserver() {
if (this.postsContainer) {
this.observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry && entry.isIntersecting) {
observer.unobserve(entry.target);
this.loadMorePosts();
this.counter();
if (this.hasNextPage) {
observer.observe(this.getLastPost());
}
}
});
});
this.observer.observe(this.getLastPost());
}
}
init() {
this.bindLoadMoreObserver();
}
}
const infiniteScroll = new InfiniteScroll();
infiniteScroll.init();
body, body * {
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
.post {
margin: 20px;
padding: 15px;
border: 1px solid #ccc;
border-radius: 5px;
}
p {
line-height: 1.5;
}
<div id="postsContainer" data-has-next-page="true" data-current-page="0" data-max-count="11">
<div class="post">
<h2>Title 1</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque dolorum consequatur nostrum sapiente ipsa! Veniam, laudantium accusantium, odio maxime quo adipisci possimus enim quam, voluptate quidem animi perferendis delectus aliquam?</p>
</div>
<div class="post">
<h2>Title 2</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque dolorum consequatur nostrum sapiente ipsa! Veniam, laudantium accusantium, odio maxime quo adipisci possimus enim quam, voluptate quidem animi perferendis delectus aliquam?</p>
</div>
<div class="post">
<h2>Title 3</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque dolorum consequatur nostrum sapiente ipsa! Veniam, laudantium accusantium, odio maxime quo adipisci possimus enim quam, voluptate quidem animi perferendis delectus aliquam?</p>
</div>
<div class="post">
<h2>Title 4</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque dolorum consequatur nostrum sapiente ipsa! Veniam, laudantium accusantium, odio maxime quo adipisci possimus enim quam, voluptate quidem animi perferendis delectus aliquam?</p>
</div>
</div>
1 Answer 1
Too complex
Always strive to keep source code complexity as low as possible.
The implementation of InfiniteScroll
is way too complex.
It should not be an instantiatable object as it only works if there is one instance, and will only work once.
Page counting need only one variable not 3
this.maxCount
,this.numberOfPages
andthis.hasNextPage
can be replace with a single counter. See rewritepageCount
which counts down.There is no need to store
iterationCount
orhasNextPage
in markup.this.postsContainer.dataset.currentPage = this.iterationCount;
Use Pseudo-class
":last-child"
to select last post.querySelector(".post:last-child")
In function
getLastPost
you reference an undefined variableconst allPosts = postsContainer.querySelectorAll(".post");
should beconst allPosts = this.postsContainer.querySelectorAll(".post");
The second argument of the
IntersectionObserver
is a reference to the same object you already have a reference to, there is no need to use the second argument.When the observer callback is called there will only be one entry that you need to handle. Rather than
entries.forEach
useentries.find
to locate the entry.
Rewrite
The rewrite does not create an object. The function InfiniteScroll holds the observer. When observer.disconnect()
is called the IntersectionObserver
callback will be de-referenced and the closure containing the InfiniteScroll
will be cleaned up automatically.
To prevent InfiniteScroll
from being called again it is de-referenced on the first call.
It works the same but with over 65% less code
function InfiniteScroll() {
InfiniteScroll = undefined;
const query = (qStr, root = document) => root.querySelector(qStr);
const lastPost = () => query(".post:last-child", container);
const perPage = 5;
const container = query("#postsContainer");
const posts = container.querySelectorAll(".post");
var pageCount = container.dataset.maxCount / perPage | 0;
const observer = new IntersectionObserver(intersect);
observer.observe(lastPost());
function intersect(entries) {
const intersecting = entries.find(entry => entry.isIntersecting);
if (intersecting) {
observer.unobserve(intersecting.target);
posts.forEach(item => container.appendChild(item.cloneNode(true)));
--pageCount ? observer.observe(lastPost()) : observer.disconnect();
}
}
}
InfiniteScroll();
body, body * {
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
.post {
margin: 20px;
padding: 15px;
border: 1px solid #ccc;
border-radius: 5px;
}
p {
line-height: 1.5;
}
<div id="postsContainer" data-has-next-page="true" data-current-page="0" data-max-count="11">
<div class="post">
<h2>Title 1</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque dolorum consequatur nostrum sapiente ipsa! Veniam, laudantium accusantium, odio maxime quo adipisci possimus enim quam, voluptate quidem animi perferendis delectus aliquam?</p>
</div>
<div class="post">
<h2>Title 2</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque dolorum consequatur nostrum sapiente ipsa! Veniam, laudantium accusantium, odio maxime quo adipisci possimus enim quam, voluptate quidem animi perferendis delectus aliquam?</p>
</div>
<div class="post">
<h2>Title 3</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque dolorum consequatur nostrum sapiente ipsa! Veniam, laudantium accusantium, odio maxime quo adipisci possimus enim quam, voluptate quidem animi perferendis delectus aliquam?</p>
</div>
<div class="post">
<h2>Title 4</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque dolorum consequatur nostrum sapiente ipsa! Veniam, laudantium accusantium, odio maxime quo adipisci possimus enim quam, voluptate quidem animi perferendis delectus aliquam?</p>
</div>
</div>