4

I am looking at many answers of checking if elements overlap, but they are not applicable.

I have a wrapper component that has a header with position fixed and children

const Wrapper = ({ children }) => {

  return (
    <>
    <Header/>
    {children}
    </>
  )
};

export default Wrapper

I need to know when the header is overlapping certain parts in several different pages (children) so as to change the header color

I am trying to detect in the wrapper component, but the elements do not exist or are not accesible to the container

Do I need to pass refs all the way from the wrapper to all the children? Is there an easier way to do this?

Thanks

user3808307
  • 2,270
  • 9
  • 45
  • 99

1 Answers1

6

there are a few approaches i can think of, one being the one you mentioned and passing refs around and doing a bunch of calculations and a few others below.

Hardcoded heights of the pages

This would work basically by having a large switch case in you header file and check the offset scroll position of your scroll target.

getBackgroundColor = (scrollPosition) => {
    switch (true) {
        case scrollPosition <= $('#page1').height:
            return 'red'
        case scrollPosition <= $('#page1').height + $('#page2').height:
            return 'blue'
        case scrollPosition <= $('#page1').height + $('#page2').height + $('page3').height
            return 'green'
        default:
            return 'yellow'
    }
}

This has obvious flaws, one being, if the page content is dynamic or changes frequently it may not work, checking height every re-render may cause reflow issues, and this requires knowledge of page IDs on the page. (note: this snippet is just to prove concept, it will need tweeks).

Intersection Observer (Recommended way)

Intersection observer is an awesome API that allows you to observe elements in a performant way and reduces layout thrashing from the alternative ways with constant measurements heres an example I made with react and intersection observer, https://codesandbox.io/s/relaxed-spence-yrozp. This may not be the best if you have hundreds of targets, but I have not ran any benchmarks on that so not certain. Also it is considered "Experimental" but has good browser support (not IE).

here is the bulk of the codesandbox below just to get an idea.

React.useEffect(() => {
    let headerRect = document.getElementById("header").getBoundingClientRect();
    let el = document.getElementById("wrapper");
    let targets = [...document.getElementsByClassName("block")];

    let callback = (entries, observer) => {
      entries.forEach(entry => {
        let doesOverlap = entry.boundingClientRect.y <= headerRect.bottom;
        if (doesOverlap) {
          let background = entry.target.style.background;
          setColor(background);
        }
      });
    };

    let io = new IntersectionObserver(callback, {
      root: el,
      threshold: [0, 0.1, 0.95, 1]
    });
    targets.forEach(target => io.observe(target));

    return () => {
      targets.forEach(target => io.unobserve(target));
    };
  }, []);

ALso notice this is not the most "React" way to do things since it relys a lot on ids, but you can get around that by passing refs everywhere i have used dom selections, but that may become unwieldy.

Community
  • 1
  • 1
Joey
  • 1,075
  • 8
  • 13