Frontend Development 21 min read

Understanding and Using the Intersection Observer API for Visibility Detection in Frontend Development

This article explains the purpose, core concepts, configuration options, callback handling, and practical use‑cases of the Intersection Observer API—such as lazy loading, infinite scrolling, sticky headers, and analytics—while providing complete Vue 3 code examples and compatibility notes.

ByteFE
ByteFE
ByteFE
Understanding and Using the Intersection Observer API for Visibility Detection in Frontend Development

The Intersection Observer API enables efficient, asynchronous detection of when a target element intersects with a root element (usually the viewport), which is essential for many modern frontend features like image lazy‑loading, infinite scroll, ad viewability, and animation triggers.

What the API Is

An IntersectionObserver instance observes one or more target elements and reports intersection changes via a callback. It focuses on geometric intersection rather than visual visibility, so CSS properties like opacity:0 or visibility:hidden do not affect isIntersecting , while display:none prevents observation.

Creating an Observer

Instantiate the observer with new IntersectionObserver(callback, options) . The callback receives an array of IntersectionObserverEntry objects and the observer itself.

const myObserver = new IntersectionObserver(callback, options);

The options object can contain:

root : The element used as the viewport; defaults to document .

rootMargin : Offsets added to the root's bounding box (e.g., '0px 0px 0px 0px' or percentages).

threshold : A number or array of numbers between 0 and 1 indicating at which visibility percentages the callback fires.

trackVisibility : When true , the observer also reports isVisible based on actual page visibility.

delay : Minimum delay (ms) before invoking the callback when trackVisibility is enabled (minimum 100 ms).

Callback and Entry Properties

The callback processes each IntersectionObserverEntry , which provides:

boundingClientRect : The target's DOMRect from getBoundingClientRect() .

intersectionRect : The intersecting area.

intersectionRatio : Ratio of intersection area to target area.

isIntersecting : Boolean indicating whether any intersection exists.

isVisible : Boolean reflecting true visual visibility (requires trackVisibility:true ).

rootBounds : Bounding box of the root element.

target : The observed DOM node.

time : Timestamp of the observation.

Observer Instance Methods

Common methods include:

observe(target) : Start observing a new element.

unobserve(target) : Stop observing a specific element.

disconnect() : Stop observing all elements.

takeRecords() : Synchronously retrieve pending entries.

Practical Scenarios (Vue 3 Examples)

Infinite Scroll : Observe a hidden reference element at the bottom of a list; when it becomes intersecting, fetch more data and append to the list.

const callback = (entries) => {
  const entry = entries[0];
  if (entry.isIntersecting) {
    // fetch more items
  }
};
const options = { root: null, threshold: [0, 1], trackVisibility: true, delay: 100 };
const observer = new IntersectionObserver(callback, options);
observer.observe(reference.value);

Image Lazy‑Loading : Use a larger rootMargin so the image starts loading before it enters the viewport.

const callback = (entries) => {
  const entry = entries[0];
  if (entry.isIntersecting) {
    url.value = 'https://example.com/image.jpg';
  }
};
const options = { rootMargin: '200px 200px 200px 200px', threshold: [0] };
const observer = new IntersectionObserver(callback, options);
observer.observe(header.value);

Sticky Header : Observe a sentinel element; when it leaves the viewport, set the header's position to fixed , otherwise revert to relative .

const callback = (entries) => {
  const entry = entries[0];
  if (!entry.isIntersecting) {
    header.value.style.position = 'fixed';
    header.value.style.top = '0px';
  } else {
    header.value.style.position = 'relative';
  }
};
const observer = new IntersectionObserver(callback, { threshold: [0, 1] });
observer.observe(reference.value);

Analytics (Exposure Tracking) : Trigger an event only when the element is at least 50 % visible.

const callback = (entries) => {
  const entry = entries[0];
  if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
    console.log('Element exposed for analytics');
  }
};
const options = { threshold: [0.5] };
const observer = new IntersectionObserver(callback, options);
observer.observe(header.value);

Compatibility

The core Intersection Observer API is supported in modern browsers, but the trackVisibility and delay properties belong to IntersectionObserver V2 and have limited support. Polyfills such as intersection-observer-polyfill can provide fallback for older environments.

References

MDN Web Docs – Intersection Observer API

Can I Use – IntersectionObserver

Ruanyifeng – IntersectionObserver tutorial

intersection-observer-polyfill (npm)

Sticky HeaderIntersectionObserverlazy-loadingWeb APIInfinite ScrollVue3Visibility Detection
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.