是什么导致无法检测到纯 JavaScript 中 HTML 元素底部的滚动?

What causes this failure to detect scrolling to the bottom of an HTML element in pure JavaScript?

我正在开发纯粹的 JavaScript 无限滚动。

我有一个 post 的 4 列表。当我到达容器底部时,我试图加载更多 posts(“原始”的克隆)。

class InfiniteScroll {
  constructor() {
    this.observer = null;
    this.isLoading = false;
    this.postsContainer = document.querySelector('#postsContainer');
    this.postsArary = postsContainer.querySelectorAll('.post');
    this.hasNextPage = this.postsContainer?.dataset?.hasNextPage === 'true';
    this.currentPage = this.postsContainer?.dataset?.currentPage;
    this.nextPage = this.hasNextPage ? this.currentPage + 1 : null;
  }

  loadMorePosts() {
    if (this.hasNextPage) {
      this.postsArary.forEach(item => {
         let postClone = item.cloneNode(true);
         this.postsContainer.appendChild(postClone);
      });
    }
  }

  bindLoadMoreObserver() {
    if (this.postsContainer) {
      this.observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
          if (entry && entry.isIntersecting) {
            this.loadMorePosts();
          }
        });
      });

      this.observer.observe(this.postsContainer);
    }
  }

  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>

问题

post 克隆不是在滚动到最后一个 post 后附加到容器中,而是在页面加载时附加。

我不想用rootMargin,因为它看起来不太通用

我做错了什么?

好的,也许它会对某人有所帮助:

class InfiniteScroll {
    constructor() {
        this.observer = null;
        this.isLoading = false;
        this.postsContainer = document.querySelector('#postsContainer');
        this.postsArary = postsContainer.querySelectorAll('.post');
        this.hasNextPage = this.postsContainer ? .dataset ? .hasNextPage === 'true';
        this.currentPage = this.postsContainer ? .dataset ? .currentPage;
        this.nextPage = this.hasNextPage ? this.currentPage + 1 : null;
    }

    loadMorePosts() {
        if (this.hasNextPage) {
            this.postsArary.forEach(item => {
                let postClone = item.cloneNode(true);
                this.postsContainer.appendChild(postClone);
            });
        } else {
            if (this.observer) this.observe.disconnect();
        }
    }

    getLastPost() {
        const allPosts = [...this.postsContainer.querySelectorAll('.post')];
        return allPosts[allPosts.length - 1];
    }
    
    bindLoadMoreObserver() {
        if (this.postsContainer) {
            this.observer = new IntersectionObserver((entries, observer) => {
                entries.forEach(entry => {
                    if (entry && entry.isIntersecting) {
                        observer.unobserve(entry.target);
                        this.loadMorePosts();    
                        observer.observe(this.getLastPost());
                    }
                });
            });

            this.observer.observe(this.getLastPost());
        }
    }

    init() {
        this.bindLoadMoreObserver();
    }
}

const infiniteScroll = new InfiniteScroll();
infiniteScroll.init();
  1. 我创建了函数 getLastPost - 它获取了你的最后一个 post(由 js 添加)。

  2. 我观察最后一个post,而不是整个容器

  3. 然后当这个项目相交时我做了两件事:取消观察当前条目元素,然后在添加新元素之后我观察最后一个 post- 所以这将是最后一个 post, 由js添加。

  4. 作为 loadMorePosts 的奖励我看到你想在有下一页时添加 posts 而不是你的 IO 仍然有效,但这不是必需的(性能).所以... disconnect-- disonnect 正在终止 IO 进程也取消观察此 IO 中的所有元素。

带有 perPage 变量和已知帖子总数的示例:

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() {
    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>