System Designmedium

Implement infinite scroll with IntersectionObserver

Implement an infinite-scrolling list that fetches the next page of data when the user reaches the bottom of the list, using IntersectionObserver instead of scroll events.

Asked at Amazon, Netflix, Meta

#IntersectionObserver#pagination#performance#async

Answer

class InfiniteScroll {
  constructor(container, fetchPage) {
    this.container = container
    this.fetchPage = fetchPage
    this.page = 1
    this.loading = false
    this.done = false

    // Sentinel element sits at the bottom of the list
    this.sentinel = document.createElement('div')
    container.appendChild(this.sentinel)

    this.observer = new IntersectionObserver(
      ([entry]) => { if (entry.isIntersecting) this._load() },
      { rootMargin: '200px 0px' }  // pre-fetch 200px before visible
    )
    this.observer.observe(this.sentinel)
  }

  async _load() {
    if (this.loading || this.done) return
    this.loading = true
    this._showSpinner()

    const items = await this.fetchPage(this.page)

    if (!items.length) {
      this.done = true
      this.observer.disconnect()
    } else {
      items.forEach(item => {
        const el = document.createElement('div')
        el.className = 'item'
        el.textContent = item.title
        this.container.insertBefore(el, this.sentinel)
      })
      this.page++
    }

    this._hideSpinner()
    this.loading = false
  }

  _showSpinner() { this.sentinel.textContent = 'Loading…' }
  _hideSpinner() { this.sentinel.textContent = '' }
}

// Usage
const container = document.querySelector('#feed')
const fetchPage = async (page) => {
  const r = await fetch('/api/feed?page=' + page)
  return r.json()
}
const infiniteList = new InfiniteScroll(container, fetchPage)
void infiniteList

Practise more System Design questions →