無限スクロール 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 };