Observers: Intersection, Mutation, Resize

Advanced · 21 min read · ▶ live playground · ✦ checkpoint

Observers are browser APIs that let JavaScript react when the page changes in ways a click handler cannot directly know. IntersectionObserver notices visibility, ResizeObserver notices element size, and MutationObserver notices DOM changes.

These APIs matter because many polished interfaces are not driven by one obvious button press. A section becomes visible while the user scrolls, a panel changes width after layout shifts, or another piece of code adds a row. Observers let the browser report those facts instead of making your code keep asking.

Observers replace repeated checking

You could ask the browser a question over and over:

setInterval(() => {
  const box = card.getBoundingClientRect();
  // Is it visible yet?
}, 100);

That is polling: your code checks on a timer whether something changed. Polling is sometimes useful, but it is wasteful for DOM conditions the browser already tracks while it lays out and paints the page.

An observer flips the direction. You create an observer with a callback, tell it what to watch, and the browser calls your callback when relevant changes are available.

const observer = new IntersectionObserver((entries) => {
  for (const entry of entries) {
    console.log(entry.isIntersecting);
  }
});
 
observer.observe(card);

The shared shape is:

  1. Create the observer with a callback.
  2. Watch one or more targets with .observe(target).
  3. Read the entries or records the browser sends.
  4. Stop later with .unobserve(target) or .disconnect().

IntersectionObserver watches visibility

IntersectionObserver reports when a target enters or leaves a visible area. By default, it compares the target to the viewport. You can also set a root element, which makes the observer watch visibility inside a scrollable container.

This is the API behind many common patterns:

  • Lazy-loading: start loading an image or section when it is almost visible.
  • Infinite scroll: request the next page when a sentinel element appears near the bottom.
  • Scroll-spy: highlight the table-of-contents item for the section currently in view.

The callback receives entries. Each entry describes one target at one observation moment. The beginner-friendly fields are entry.target, entry.isIntersecting, and entry.intersectionRatio.

Use it when visibility is the question. Do not wire a scroll listener that reads layout on every scroll frame unless you truly need custom frame-by-frame behavior.

ResizeObserver watches element size

ResizeObserver reports when an element's box changes size. That is different from window.resize: an element can resize because a sidebar opened, text wrapped, a parent changed grid columns, or a class changed its width.

The callback receives entries with size information. For everyday UI, entry.contentRect.width and entry.contentRect.height are the approachable starting point.

Resize observers fit component-level layout: a chart can switch labels when its own card gets narrow, a toolbar can collapse actions when its container shrinks, or a preview can update measurements after content changes.

MutationObserver watches DOM changes

MutationObserver reports when watched DOM nodes change. It can notice child elements being added or removed, attributes changing, or text changing, depending on the options you choose.

This is not the main pattern for your own state-render UI. When your code owns the state, update state and render from it like you did in 17.2. MutationObserver is for boundary cases: watching content another script owns, responding to a third-party widget, or debugging unexpected DOM changes.

The callback receives mutation records. A record tells you what kind of change happened: childList, attributes, or characterData.

Try it - one observer lab

Run the playground, then try each control group. The first row scrolls a watched card into or out of view. The second row resizes a watched panel. The third row changes a list so the mutation observer can report DOM records.

web page — live previewsandboxed
Run rebuilds the page

The lab uses three observers, but the pattern stays small. Each observer watches a specific target, each callback updates visible text, and each callback writes one log line.

Notice what the code does not do. It does not run a timer to ask whether the watched card moved. It does not listen for every possible window resize and guess which panel changed. It does not check the list over and over to see whether another handler edited it. The browser batches the relevant facts and sends them to the callback.

Choose the observer by the question

Use IntersectionObserver when your question is "is this thing visible enough?" It is the right fit for lazy-loading, scroll-spy navigation, reveal animations, and the sentinel element at the end of an infinite list. Infinite scroll still needs the failure handling you learned in Section 16; the observer only tells you when it is time to ask for more data.

Use ResizeObserver when your question is "did this element's size change?" It is better than tying component behavior to the whole window size, because a card can become narrow even when the browser window did not change.

Use MutationObserver when your question is "did this DOM subtree change outside my normal render path?" It is powerful, but it is not a replacement for clean state. If your own code controls the data, the state-render loop from 17.2 is usually clearer.

Clean up observers

Observers keep references to the things they watch. If a component goes away, disconnect its observers just like you removed event listeners and cleared timers earlier in the course.

const observer = new ResizeObserver(handleResize);
observer.observe(panel);
 
// Later, when this UI is removed:
observer.disconnect();

For one target, .unobserve(panel) stops watching that target. .disconnect() stops the whole observer. This cleanup habit matters in long-lived pages where views mount, unmount, and mount again.

Observers are a bridge between your JavaScript and the browser's rendering work. Next, you will move from watching the page to drawing and using richer browser capabilities with canvas, media, and device APIs.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion