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 tells you when an element enters or leaves the viewport, asynchronously and without scroll handlers.
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
const obs = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.src = e.target.dataset.src;
obs.unobserve(e.target); // once is enough
}
});
}, { rootMargin: '200px' });2
document.querySelectorAll('img[data-src]').forEach(el => obs.observe(el));3
What it replaced. A scroll listener calling getBoundingClientRect on every element, on every scroll
event — which forces layout, runs hundreds of times a second, and is the standard cause of janky scrolling.
getBoundingClientRect
4
The observer runs off the main thread's critical path and reports only when the state changes.
5
rootMargin is the option that matters. '200px' starts loading 200 pixels before the element is
visible, so the image is there by the time the user reaches it.
rootMargin'200px'
6
Without it, loading begins when the element is already on screen and the user watches it appear.
7
threshold decides how much must be visible.
threshold: 0 any part (the default)
threshold: 0.5 half
threshold: [0, 0.5, 1] fire at eachthreshold
8
For an analytics "was this seen" event, 0.5 with a dwell time is closer to what was meant than 0.
0.50
9
Infinite scroll is a sentinel.
const sentinel = document.querySelector('#load-more');
new IntersectionObserver(([e]) => {
if (e.isIntersecting) loadNextPage();
}).observe(sentinel);10
An empty element after the last item. When it appears, fetch more.
11
Native lazy loading covers the simple case.
<img loading="lazy" src="...">12
For images and iframes that is enough, and no JavaScript is needed. The observer is for everything else:
analytics, animations on entry, video autoplay, and infinite scroll.
13
Two cautions. Unobserve elements you are done with, or the observer holds references to nodes that have
been removed. And the callback receives all changed entries in a batch, so it must handle several at
once — including elements that just left the viewport, where isIntersecting is false.
isIntersecting
14
What the interviewer is probing.1. "Why is this better than a scroll handler?" Probing: the cost. Stalls: "It is newer."
Moves up: it is handled off the main thread and fires only on threshold crossings, where a scroll
handler runs constantly and forces layout reads.
15
2. "What does rootMargin do?" Probing: the option. Stalls: "Sets the area." Moves up: it
grows or shrinks the trigger area, so you can start loading before the element is actually visible.
16
3. "How do you stop observing once loaded?" Probing: cleanup. Stalls: "It stops itself."
Moves up: call unobserve on that element in the callback, or the entry keeps firing on every
crossing.
17
4. "What else is it good for besides images?" Probing: breadth. Stalls: "Nothing." *Moves
up:* infinite scroll, analytics impressions, and pausing animations or video that scroll out of
view.