All topics
DOMintermediate

Intersection Observer for Lazy Loading

An efficient, asynchronous browser API for detecting when an element enters or exits the viewport, commonly used for lazy loading and infinite scroll.

IntersectionObserver is a browser API purpose-built for efficiently detecting when a target element becomes visible within (or scrolls out of) the viewport or another specified ancestor element, without the severe performance cost of the older technique of manually calculating element positions on every scroll event, which fires extremely frequently and forces expensive synchronous layout reads if done naively. It's a strong, practical interview topic since it directly relates to real performance work like lazy-loading images and infinite scroll.

IntersectionObserver is like a lifeguard using peripheral vision to notice the instant someone crosses into the deep end (a threshold), rather than manually re-measuring everyone's distance from the deep-end line every single second (polling on scroll) — the lifeguard is only actively alerted exactly when it actually matters.

Key Concepts

1
You create an observer with new IntersectionObserver(callback, options), then call .observe(targetElement) for each element you want to watch. The options object commonly includes a root (the ancestor element used as the viewport for intersection checking, defaulting to the browser viewport itself), a rootMargin (extends or shrinks the root's effective bounding box, useful for triggering a bit before an element is actually visible, like starting an image load slightly ahead of scroll position), and a threshold (what percentage of the target must be visible before the callback fires — 0 for 'any pixel visible,' 1 for 'fully visible,' or an array for multiple trigger points).
new IntersectionObserver(callback, options).observe(targetElement)optionsrootrootMargin
2
The callback receives an array of IntersectionObserverEntry objects, each describing one observed element's current intersection state, most importantly entry.isIntersecting (a boolean) and entry.target (the actual DOM element). This makes lazy-loading images straightforward: give each <img> a data-src attribute instead of src, observe it, and swap in the real src (then unobserve that element, since it doesn't need to be watched anymore) only once isIntersecting becomes true.
IntersectionObserverEntryentry.isIntersectingentry.target<img>data-src
3
Crucially, unlike scroll-event-based position calculations, the browser computes intersection changes off the main thread as part of its own rendering pipeline and only invokes the callback asynchronously when an actual threshold crossing happens, which makes it dramatically cheaper than naive scroll-position math repeated on every scroll event — a textbook case of using a purpose-built browser API instead of reimplementing similar behavior manually and inefficiently.