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.
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)
ByteFE
Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.