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.numberOfPagesandthis.hasNextPagecan be replace with a single counter. See rewritepageCountwhich counts down.There is no need to store
iterationCountorhasNextPagein markup.this.postsContainer.dataset.currentPage = this.iterationCount;Use Pseudo-class
":last-child"to select last post.querySelector(".post:last-child")In function
getLastPostyou reference an undefined variableconst allPosts = postsContainer.querySelectorAll(".post");should beconst allPosts = this.postsContainer.querySelectorAll(".post");The second argument of the
IntersectionObserveris 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.forEachuseentries.findto 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>