JavaScript application bug: counter variable skips a value

Viewed 165

I am working on a pure JavaScript infinite scroll. 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 = [...this.postsArary];
    return allPosts[allPosts.length - 1];
  }

  counter() {
     console.log(this.iterationCount);
    if (this.hasNextPage) {
      this.iterationCount++;
      this.postsContainer.dataset.currentPage = this.iterationCount;
    }

    this.postsContainer.dataset.hasNextPage = this.hasNextPage;
    this.hasNextPage = this.iterationCount < this.numberOfPages;
  }

  bindLoadMoreObserver() {
    if (this.postsContainer) {
      this.observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
          if (entry && entry.isIntersecting) {
            observer.unobserve(entry.target);
            this.counter();
            this.loadMorePosts();
            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="1" 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>

The problem

There is an issue with the iterationCount variable: it does not seem to reach the value 2.

I have not been able to spot the cause of that.

What am I doing wrong?

1 Answers

The array postsArary is initialized in the constructor and never updated: getLastPost() always returns the fourth post instead of the last one that was added dynamically. Consequently, the IntersectionObserver callback is only triggered by the fourth post and not by the one on the bottom of the page.

The updated getLastPost() method:

getLastPost() {
  const allPosts = postsContainer.querySelectorAll('.post');
  return allPosts[allPosts.length - 1];
}

This doesn't completely fix the issue. The third page still won't load because counter() is called before loadMorePosts(). So for the final page, hasNextPage is already false when loadMorePosts() should still load more posts.

The updated callback:

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());
    }
  }
Related