IntersectionObserver Options Reference

root, rootMargin and threshold options with bounding-box and intersection-ratio notes.

Reference for IntersectionObserver constructor options — root, rootMargin and threshold — plus every IntersectionObserverEntry property and a live rootMargin validator for lazy-loading and scroll triggers. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What does the threshold option do?

threshold sets the visibility ratio(s) at which the callback fires. A value of 0 fires as soon as a single pixel is visible, 1 fires only when the target is fully visible, and an array such as [0, 0.5, 1] fires at each step, letting you react to partial visibility.

Configuring when an element counts as visible

IntersectionObserver watches when target elements enter or leave a root viewport and reports it asynchronously, replacing janky scroll listeners. Its behaviour is shaped by three constructor options and read through the IntersectionObserverEntry passed to your callback. This reference documents both, with a live validator for the tricky rootMargin string.

How it works

You create an observer with options, then observe one or more targets:

const io = new IntersectionObserver(
  (entries) => {
    for (const entry of entries) {
      if (entry.isIntersecting) loadImage(entry.target);
    }
  },
  { root: null, rootMargin: "0px 0px -100px 0px", threshold: [0, 0.5, 1] }
);
io.observe(document.querySelector(".lazy"));

root defines the viewport (the browser viewport when null). rootMargin expands or contracts that box — a negative bottom margin makes the callback fire only once the target is well inside view. threshold lists the visibility ratios that trigger the callback, so you can react at 0%, 50% and 100% visibility.

The three options in depth

root

The root is the element that acts as the viewport. Pass null (the default) to use the browser’s actual viewport. Pass a DOM element (such as a scrollable container) to observe visibility within that element. The target must be a descendant of the root.

rootMargin

rootMargin is a CSS-margin-like string that expands or shrinks the root’s bounding box before intersection is computed. It accepts one to four values in px or % only — no other units, no bare numbers. The order is top right bottom left, the same as CSS margin shorthand.

Common patterns:

"0px"                   // no change — fire when any pixel enters the viewport
"0px 0px -200px 0px"   // shrink the bottom by 200px — fire only once 200px inside
"100px"                 // expand all sides by 100px — fire 100px before element enters

The negative-bottom pattern is the most useful for lazy-loading: it ensures images are loaded when they are 200 px away from entering view, avoiding a visible load flash.

threshold

threshold is a number or array of numbers between 0 and 1 representing the visibility ratio at which callbacks fire.

  • 0 — fire as soon as a single pixel is visible (the default)
  • 1 — fire only when the element is fully visible within the root
  • [0, 0.25, 0.5, 0.75, 1] — fire at each 25% visibility step

Use a threshold array to drive scroll-based animations: at each step you receive the entry’s intersectionRatio and can update CSS variables, opacity, or transforms.

IntersectionObserverEntry properties

Each callback invocation passes an array of IntersectionObserverEntry objects. Key properties:

PropertyTypeWhat it tells you
isIntersectingbooleanWhether the target currently overlaps the root
intersectionRationumber 0–1Fraction of the target visible within the root
boundingClientRectDOMRectReadOnlyThe target’s bounding box
intersectionRectDOMRectReadOnlyThe visible portion of the target
rootBoundsDOMRectReadOnlyThe root’s bounding box after margin
targetElementThe observed element
timeDOMHighResTimeStampWhen the change was detected

Tips and notes

  • rootMargin uses px or % only — never bare numbers or other units.
  • Pass a threshold array to animate smoothly as an element scrolls through view.
  • Read intersectionRatio for how much is visible and boundingClientRect for where it sits.
  • Call io.unobserve(target) after a one-shot trigger (like lazy-load) to stop further callbacks.
  • Observe multiple elements on a single observer instance — more efficient than creating one observer per element.
  • IntersectionObserver is widely supported in all modern browsers; check compatibility only if you need to support very old environments.