Skip to content

Conversation

@chernodub
Copy link
Contributor

@chernodub chernodub commented Oct 16, 2025

Description

Faced an issue with the threshold explanation. The intersection observer callback is actually triggered when the targetRect "touches" the rootBounds. So I tried to add this detail here in the docs.

Looking at the definition options https://w3c.github.io/IntersectionObserver/#intersection-observer-init, I can see

threshold, of type (double or sequence<double>), defaulting to 0
List of threshold(s) at which to trigger callback. callback will be invoked when intersectionRect’s area changes from greater than or equal to any threshold to less than that threshold, and vice versa.

Also, looking at https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo

  1. Let isIntersecting be true if targetRect and rootBounds intersect or are edge-adjacent, even if the intersection has zero area (because rootBounds or targetRect have zero area).

Motivation

The original explanation did not represent the actual behavior

Additional details

Simple code reproduction
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Intersection Observer Simple Demo</title>
    <style>
      .container {
        width: 300px;
        height: 300px;
        overflow-y: auto;
      }
      .page {
        width: 100%;
        height: 300px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 2em;
        color: white;
      }
      .page1 {
        background: #4287f5;
      }
      .page2 {
        background: #42f57b;
      }
      .page3 {
        background: #f5a142;
      }
    </style>
  </head>
  <body>
    <div class="container" id="container">
      <div class="page page1" id="page1">Page 1</div>
      <div class="page page2" id="page2">Page 2</div>
      <div class="page page3" id="page3">Page 3</div>
    </div>
    <div id="log" style="font-family: monospace; margin-top: 10px"></div>
    <script>
      const log = document.getElementById('log');
      function logger(txt) {
        log.innerHTML = log.innerHTML + '<br/>' + txt;
      }

      const THRESHOLD = 0;

      function getLastIntersectionObserverEntry(entries) {
        return entries[entries.length - 1];
      }

      function handleViewability({ page, isViewable }) {
        logger(`Page: ${page} isViewable: ${isViewable}`);
      }

      document.querySelectorAll('.page').forEach((pageDiv) => {
        const observer = new IntersectionObserver(
          (entries) => {
            const lastEntry = getLastIntersectionObserverEntry(entries);
            if (!lastEntry) return;

            // Following @initViewabilityTrackers.ts
            const isViewable =
              lastEntry.isIntersecting &&
              lastEntry.intersectionRatio >= THRESHOLD;

            handleViewability({
              page: pageDiv.textContent,
              isViewable,
            });
          },
          {
            root: document.getElementById('container'),
            threshold: THRESHOLD,
          }
        );

        observer.observe(pageDiv);
      });
      document.querySelectorAll('.page').forEach((p) => observer.observe(p));
    </script>
  </body>
</html>

@chernodub chernodub requested a review from a team as a code owner October 16, 2025 10:01
@chernodub chernodub requested review from wbamberg and removed request for a team October 16, 2025 10:01
@github-actions github-actions bot added Content:WebAPI Web API docs size/xs [PR only] 0-5 LoC changed labels Oct 16, 2025
@github-actions
Copy link
Contributor

@sideshowbarker sideshowbarker merged commit 648bee8 into mdn:main Oct 23, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Content:WebAPI Web API docs size/xs [PR only] 0-5 LoC changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants