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.