0

I am trying to implement an intersectionObserver to watch if a <Header/> isIntersecting, but specifically only with another <Component/>

MY ATTEMPT

import React, { useRef, useEffect } from "react";
import "./styles.css";
import Header from "./components/Header";
import SomeComponent from "./components/SomeComponent";

export default function App() {
  const header = useRef(null);
  const component = useRef(null);

  const handleOverlap = (entries, observer) => {
    entries.forEach((entry) => {
      console.log(entry.isIntersecting);
    });
  };

  useEffect(() => {
    const options = {
      root: component.current,
      rootMargin: "10px",
      threshold: 0.1
    };
    const observer = new IntersectionObserver(handleOverlap, options);
    observer.observe(header.current);
  }, []);

  return (
    <div className="App">
      <Header ref={header} />
      <div style={{ backgroundColor: "pink", height: "100vh" }}></div>
      <SomeComponent ref={component} />
    </div>
  );
}

My desired result is to receive a true value if at point the user is scrolling and the header component intersects with the target component. I have set the root in the options object to a ref of the target component. I'm definitely missing some key logic somewhere. Any help would be great.

SANDBOX

https://codesandbox.io/s/friendly-shirley-u603gt?file=/src/App.js

Ben_Sven_Ten
  • 529
  • 3
  • 21

1 Answers1

0

Sharing my solution in case anyone should ever be in a similar situation.

My solution was to check for a scroll event as opposed to using the intersectionObserver I believe because my <Header/> is a fixed position element, it is taken out of the document flow and the observer can not detect a valid intersection between the two components.

Instead, my checkIfHeaderIsOverlapping function calls the .getBoundingClientRect() on the ref of each component, and applies some simple logic to detect an overlap/interection.

SOLUTION

import React, { useRef, useEffect, useState } from "react";
import "./styles.css";
import Header from "./components/Header";
import SomeComponent from "./components/SomeComponent";

export default function App() {
  const [isOverlapping, setIsOverlapping] = useState(false);
  console.log("is it true? =>", isOverlapping);

  const header = useRef(null);
  const component = useRef(null);

  function checkIfHeaderIsOverlapping() {
    if (header.current && component.current) {
      const a = header.current.getBoundingClientRect();
      const b = component.current.getBoundingClientRect();

      if (a.top <= b.top + b.height && a.top + a.height > b.top) {
        setIsOverlapping(true);
      } else {
        setIsOverlapping(false);
      }
    }
  }

  useEffect(() => {
    function watchScroll() {
      window.addEventListener("scroll", checkIfHeaderIsOverlapping);
    }
    watchScroll();

    return () => {
      window.removeEventListener("scroll", checkIfHeaderIsOverlapping);
    };
  });

  return (
    <div className="App">
      <Header ref={header} />
      <div style={{ backgroundColor: "pink", height: "100vh" }}></div>
      <SomeComponent ref={component} />
      <div style={{ backgroundColor: "green", height: "100vh" }}></div>
    </div>
  );
}

SANDBOX

https://codesandbox.io/s/friendly-shirley-u603gt?file=/src/App.js

Ben_Sven_Ten
  • 529
  • 3
  • 21
  • Yes `position: absolute` has a an effect (but merely needs an appropriate `relative` position assigned to the observing root), but as noted above, the main problem is that `target` must be a descendant of `root`, not a sibling. Per the [docs](https://w3c.github.io/IntersectionObserver/#intersection-observer-interface): *'An IntersectionObserver with a non-null root is referred to as an explicit root observer, and it can observe any target Element that is a descendant of the root in the containing block chain.'* – pilchard Apr 09 '22 at 16:11
  • @pilchard in this case my two components are siblings? How can I properly set the ```target``` then? – Ben_Sven_Ten Apr 09 '22 at 16:13
  • 1
    The `root` would be the container of both and the `target` would be the component with a top threshold set to match the header height. possibly see: [IntersectionObserver and position: absolute](https://stackoverflow.com/questions/65736386/intersectionobserver-and-position-absolute) and [Change style header/nav with Intersection Observer (IO)](https://stackoverflow.com/questions/57834100/change-style-header-nav-with-intersection-observer-io) – pilchard Apr 09 '22 at 16:17