IntersectionObserver with root null trigerred on intersection with parent element

I’m exploring the IntersectionObserver API and can’t figure out how the root:null option works. The documentation says that if null is passed, the observer will watch for intersection relative to the device’s viewport. But in my example, the callback function is executed when the element is crossing the boundaries of the parent element, not the viewport. Why is this happening?

Here is example

const second = document.querySelector('.second');

const options = {
  root: null, // to watch for intersection relative to the device's viewport
  threshold: 1,
};

const callback = (entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      console.log('second element is intersecting with VIEWPORT');
    } else {
      console.log('second element is NOTintersecting with VIEWPORT');
    }
    
    // Each entry describes an intersection change for one observed
    // target element:
    //   entry.boundingClientRect
    //   entry.intersectionRatio
    //   entry.intersectionRect
    //   entry.isIntersecting
    //   entry.rootBounds
    //   entry.target
    //   entry.time
  });
};

const observer = new IntersectionObserver(callback, options);

observer.observe(second)
body {
  background: gray;
}
.wrapper {
  border: 2px solid red;
  margin: 40px;
  height: calc(100vh - 80px);
  overflow-y: scroll;
}

.first,
.second,
.third {
  height: 40vh;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: #2d1176;
  border-bottom: 15px solid black;
  color: #fff;
}

.third {
  height: 1000vh;
}
<div class="wrapper">
  <section class="first">
    <h1>⬇ First ⬇</h1>
  </section>
  <section class="second">
    <h1>⬇ Second (observable) ⬇</h1>
  </section>
  <section class="third">
    <h1>⬆︎ Third ⬆︎</h1>
  </section>
</div>

Just scroll the content to the second section and you will see “second element is intersecting with VIEWPORT” in console. Why does it happen? This intersection is not with the viewport, but with the parent element. But I passed null in root argument.

Leave a Comment