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

Posted

技术标签:

【中文标题】是啥导致无法在纯 JavaScript 中检测到滚动到 HTML 元素的底部?【英文标题】:What causes this failure to detect scrolling to the bottom of an HTML element in pure JavaScript?是什么导致无法在纯 JavaScript 中检测到滚动到 HTML 元素的底部? 【发布时间】:2021-07-04 08:18:45 【问题描述】:

我正在开发一个纯 javascript 无限滚动。

我有 4 个帖子列表。当我到达容器底部时,我正在尝试加载更多帖子(“原始”帖子的克隆)。

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>

问题

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

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

我做错了什么?

【问题讨论】:

您的postsContainer 在加载时与页面相交,因此您得到的是正确的。尝试在postsContainer 上检查intersectionRatio,然后克隆项目。 【参考方案1】:

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

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

    我创建了函数getLastPost - 它获取你的最后一篇文章(包含由 js 添加)。

    我观察最后一个帖子,而不是整个容器

    然后当这个项目相交时,我做了两件事:不观察当前条目元素,然后在添加新元素后我观察最后一篇文章-所以这将是最后一篇文章,由 js 添加。

    作为 loadMorePosts 的奖励,我看到您只想在有下一页可用时添加帖子。但是当您的 IO 不可用时,您的 IO 仍然有效,但这不是必需的(性能)。所以...断开连接-断开连接正在杀死 IO 进程,也未观察此 IO 中的所有元素。

【讨论】:

【参考方案2】:

带有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>

【讨论】:

以上是关于是啥导致无法在纯 JavaScript 中检测到滚动到 HTML 元素的底部?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在纯 PHP 中检测循环数组?

有没有办法在纯 PHP 中检测循环数组?

Javascript检测div之外的点击事件

在 Javascript 中进行浏览器检测的最佳方法是啥?

如何在纯 JavaScript 中切换元素的类?

$(document).on() 在纯 JavaScript 中?