I have tried to answer your problem as I understand it. I have filled in some extra pieces to help give a full solution. I hope bits and pieces of this are relevant to your issue!
So if I understand you correctly you want to:
- be able to automatically update the active tab in your navbar when a user scrolls the page. Also, I assume they are scrolling vertically for my answer.
As per the problem and code you provided, I infer you have a setup somewhat like this:
- A Section or Category component containing content for a given item
- A Page component that would render a number of Cateogory components and NavBar
Given these two things, I have put the following code together that could help you achieve the functionality you are looking for:
import React, { useEffect, useRef, useState } from "react";
/** Check if an HTML element is within the main focus region of the page */
function isElementInMainFocusArea(
element,
verticalConstrainFactor = 0,
horizontalConstrainFactor = 0
) {
const elementRect = element.getBoundingClientRect();
const documentHeight =
window.innerHeight || document.documentElement.clientHeight;
const documentWidth =
window.innerWidth || document.documentElement.clientWidth;
// Vertical focus region
const topFocusPos = documentHeight * verticalConstrainFactor;
const bottomFocusPos = documentHeight * (1 - verticalConstrainFactor);
// Horizontal focus region
const leftFocusPos = documentWidth * horizontalConstrainFactor;
const rightFocusPos = documentWidth * (1 - horizontalConstrainFactor);
return (
elementRect.top >= topFocusPos &&
elementRect.bottom <= bottomFocusPos &&
elementRect.left >= leftFocusPos &&
elementRect.right <= rightFocusPos
);
}
/** Navigation bar component which will taken in a list of refs.
Each ref must be assigned to or given as a prop to some other section on the page that we want to scroll to. */
function NavBar({ pageSectionRefs, categories }) {
const [currentCat, setCurrentCat] = useState();
/** Set up the scroll event listener to update the currentCat whenever we scroll*/
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
/** Check all the refs we are to watch to see which one of them is in focus */
function handleScroll() {
for (ref in pageSectionRefs.reverse()) {
if (isElementInMainFocusArea(ref.current)) {
setCurrentCat(index);
return; // if two elements are in focus, only the first will be chosen. Could cause a glitch
}
}
}
// Returns what you were previously returning for this component
return (
<div
className={
Position ? "navbarContainer navbarContainerScroll" : "navbarContainer"
}>
{categories.map((item) => (
<ul>
<li
className={
item.Cod === currentCat
? "navbarContainer liSelect"
: "navbarContainer liNormal"
}
onClick={() => onHandleClick(item.Cod)}>
{item.Nome}
</li>
</ul>
))}
</div>
);
}
/** The top level component that will be rendering the NavBar and page sections. */
function MyPage() {
const categories = [];
const pageSectionRefs = categories.map(() => useRef());
return (
<div>
<NavBar pageSectionRefs={pageSectionRefs} categories={categories} />
{categories.map((cat, idx) => {
return (
<div key={idx} ref={pageSectionRefs[idx]}>
Section for category {idx}
</div>
);
})}
</div>
);
}
In case I don't have a full picture of your problem, kindly comment things I may have missed so I update my answer!
References: