無限スクロール infinite scroll 「intersection observer」

適当に実装してみた

option で渡す root は null の場合と任意の場合があるので場合分け
いずれもその viewport 内の最後の要素を監視
上にスクロールして戻る時は監視不要
よってその都度監視を停止して新しい監視を開始

class InfiniteScroller {
  Id = 0;
 constructor(option) {
    const observer = new IntersectionObserver(
      this.onIntersectedHandler.bind(this),
      option
    );

    if (option.root === null) {
      observer.observe(document.querySelector('body').lastElementChild);
    } else {
      observer.observe(option.root.lastElementChild);
    }

    this.viewport = option.root;
  }
  // 交差したら呼ばれる
  async onIntersectedHandler(entries, observer) {
    const entry = entries[0];
    if (entry.isIntersecting) {
      const contentList = await this.fetchContent(this.Id);

      this.appendArticle(contentList);

      observer.disconnect();

      let newTarget;
      if (this.viewport === null) {
        newTarget = document.querySelector('body').lastElementChild;
      } else {
        newTarget = document.querySelector(`.${this.viewport.className}`)
          .lastElementChild;
      }
      observer.observe(newTarget);

      this.Id++;
    }
  }
  // コンテンツ追加
  appendArticle(contentList) {
    let newViewport;
    if (this.viewport === null) {
      newViewport = document.querySelector('body');
    } else {
      newViewport = document.querySelector(`.${this.viewport.className}`);
    }
    contentList.forEach((content) => {
      newViewport.insertAdjacentHTML(
        'beforeend',
        `<article class="article">
              <img class="article__img" src="sample.jpg">
              <p>${JSON.parse(content)}</p>
         <article>
        `
      );
    });
  }
  // コンテンツ取得
  async fetchContent(Id) {
    try {
      const res = await fetch(
        `http://localhost?Id=${Id}`
      );
      if (!res.ok) {
        throw new Error(res.status);
      }
      return await res.json();
    } catch (err) {
      console.log(err);
    }
  }
}
export { InfiniteScroller };