Setting root property in options argument of Intersection Observer causes weird behavior

Viewed 741

I've been testing out the Intersection Observer API specifically, in React. I'm running into an issue where, when I set the options argument's root property, the observer cannot properly identify when elements are visible. If that wasn't entirely clear, please see my code below:

import * as React from "react"
import {} from "styled-components/macro"

const Component = () => {
  
  const containerRef = React.useRef()

  React.useEffect(() => {
     const container = containerRef.current
     
     if(container){
       
       const observer = new IntersectionObserver((entries) => {
         entries.forEach((entry) => {
           console.log(entries.intersectionRatio)
         })
       }, {
       root: container,
       threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
       })
   
       container.childNodes.forEach((child) => {
         observer.observe(child)
       })

       return () => observer.disconnect()
     }
  }, [])
  
  
 return <div
      
      ref={containerRef}
      css={`
        background-color: green;
        padding: 15px;

        > * {
          margin-bottom: 20px;
        }
      `}
    >
      <h1 name="section1">
        Section One
      </h1>
      <p name="para1">
        sit amet tincidunt eu, consectetur non purus. Ut ex lorem, pellentesque
        quis augue quis, semper fringilla ante. Nunc suscipit interdum nisl, id
        fermentum turpis consequat pellentesque. Praesent nec ex egestas tellus
        gravida vulputate nec sed tortor. Maecenas vulputate mattis tincidunt.
      </p>

      <h2 name="section2">Section Two</h2>
      <p name="para2">
        Ut commodo ac turpis eu cursus. Aliquam vel faucibus leo. Etiam ac
        tortor ullamcorper, ornare nisl in, blandit nisl. Quisque congue dictum
        eros at pellentesque. Morbi vulputate massa ac sapien molestie, vitae
        dictum elit varius. Quisque efficitur pulvinar turpis, ac condimentum

      </p>

      <h3 name="section3">Section 3</h3>
      <p name="para3">
        Ut commodo ac turpis eu cursus. Aliquam vel faucibus leo. Etiam ac
        tortor ullamcorper, ornare nisl in, blandit nisl. Quisque congue dictum
        eros at pellentesque. Morbi vulputate massa ac sapien molestie, vitae
 
      </p>
    </div>
}

This is a pretty basic example of what I'm trying to do. The problem here is that it shows that all elements have a ratio of '1.0' which makes no sense since some of the elements are out of the viewport. Another issue is that the thresholds aren't triggering the callback except on the initial observation. This code works if I set root as null BUT for reasons I won't go into, that is not ideal. Setting root as null defaults the viewport to the entire body of the document.

I can't seem to figure exactly what the issue is, I'd appreciate any help in getting this resolved.

1 Answers

Tried your component with some changes, it works fine.

Seems console.log(entries.intersectionRatio) is not correct from your code

const Component = () => {
  
  const containerRef = React.useRef()

  React.useEffect(() => {
     const container = containerRef.current
     
     if(container){
       
       const observer = new IntersectionObserver((entries) => {
         entries.forEach((entry) => {
           const {isIntersecting, target, intersectionRatio } = entry;
           console.log( {isIntersecting, name: target.attributes["name"].value, intersectionRatio });
         })
       }, {
       root: container,
       threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
       })
   
       container.childNodes.forEach((child) => {
         observer.observe(child)
       })

       return () => observer.disconnect()
     }
  }, [])
  
  
 return <div
      
      ref={containerRef}
      style={{
        padding: '15px',
        height: '200px',
        overflow: 'scroll',
      }}
    >
      <h1 name="section1">
        Section One
      </h1>
      <p name="para1">
        sit amet tincidunt eu, consectetur non purus. Ut ex lorem, pellentesque
        quis augue quis, semper fringilla ante. Nunc suscipit interdum nisl, id
        fermentum turpis consequat pellentesque. Praesent nec ex egestas tellus
        gravida vulputate nec sed tortor. Maecenas vulputate mattis tincidunt.
      </p>

      <h2 name="section2">Section Two</h2>
      <p name="para2">
        Ut commodo ac turpis eu cursus. Aliquam vel faucibus leo. Etiam ac
        tortor ullamcorper, ornare nisl in, blandit nisl. Quisque congue dictum
        eros at pellentesque. Morbi vulputate massa ac sapien molestie, vitae
        dictum elit varius. Quisque efficitur pulvinar turpis, ac condimentum

      </p>

      <h3 name="section3">Section 3</h3>
      <p name="para3">
        Ut commodo ac turpis eu cursus. Aliquam vel faucibus leo. Etiam ac
        tortor ullamcorper, ornare nisl in, blandit nisl. Quisque congue dictum
        eros at pellentesque. Morbi vulputate massa ac sapien molestie, vitae
 
      </p>
    </div>
}

I was able to observe invisible element's results as isIntersecting: false, intersectionRation: 0

Related