I am new to React, so apologies if this is obvious.
I have several components on a single-page website. This is my App.js:
function App() {
return (
<>
<Header />
<Nav />
<About />
<Experience />
<Portfolio />
<Activities />
<Contact />
<Footer />
</>
);
}
This is the Nav component:
const Nav = () => {
const [activeNav, setActiveNav] = useState('#');
return (
<nav>
<a href='#' onClick={() => setActiveNav('#')} className={activeNav === '#'? 'active': ''}><BiHome/></a>
<a href='#about' onClick={() => setActiveNav('#about')} className={activeNav === '#about'? 'active': ''}><AiOutlineUser/></a>
<a href='#experience' onClick={() => setActiveNav('#experience')} className={activeNav === '#experience'? 'active': ''}><MdWorkOutline/></a>
<a href='#portfolio' onClick={() => setActiveNav('#portfolio')} className={activeNav === '#portfolio'? 'active': ''}><AiOutlineCode/></a>
<a href='#contact' onClick={() => setActiveNav('#contact')} className={activeNav === '#contact'? 'active': ''}><BiMessageSquareDetail/></a>
</nav>
)
}
As you can see, it has links to the other components. I have styled it as a floating navbar. When the user clicks on a link, they're taken to that section of the page. The link's class is changed to "active" - its CSS gets changed to distinguish it from the rest.
Now I want to do it the other way around. When a user scrolls to a component, say Experience, I want the class of the corresponding link in Nav to be changed to active. How can I do that?
Thanks very much for reading until the end :)
CodePudding user response:
maybe you can use onScroll event to calculate the difference between component's height and page's scrollTop in real time
CodePudding user response:
Maybe use the useEffect
hook to listen for changes in the scroll position of the page and then updating the state of the activeNav
based on the position of the different sections of the page
import { useEffect } from 'react';
const Nav = () => {
const [activeNav, setActiveNav] = useState('#');
useEffect(() => {
const handleScroll = () => {
const aboutSection = document.querySelector('#about');
const experienceSection = document.querySelector('#experience');
const portfolioSection = document.querySelector('#portfolio');
const contactSection = document.querySelector('#contact');
if (aboutSection.getBoundingClientRect().top < window.innerHeight * 0.8 && aboutSection.getBoundingClientRect().top > 0) {
setActiveNav('#about');
} else if (experienceSection.getBoundingClientRect().top < window.innerHeight * 0.8 && experienceSection.getBoundingClientRect().top > 0) {
setActiveNav('#experience');
} else if (portfolioSection.getBoundingClientRect().top < window.innerHeight * 0.8 && portfolioSection.getBoundingClientRect().top > 0) {
setActiveNav('#portfolio');
} else if (contactSection.getBoundingClientRect().top < window.innerHeight * 0.8 && contactSection.getBoundingClientRect().top > 0) {
setActiveNav('#contact');
}
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [setActiveNav]);
return (
<nav>
<a href='#' onClick={() => setActiveNav('#')} className={activeNav === '#'? 'active': ''}><BiHome/></a>
<a href='#about' onClick={() => setActiveNav('#about')} className={activeNav === '#about'? 'active': ''}><AiOutlineUser/></a>
<a href='#experience' onClick={() => setActiveNav('#experience')} className={activeNav === '#experience'? 'active': ''}><MdWorkOutline/></a>
<a href='#portfolio' onClick={() => setActiveNav('#portfolio')} className={activeNav === '#portfolio'? 'active': ''}><AiOutlineCode/></a>
<a href='#contact' onClick={() => setActiveNav('#contact')} className={activeNav === '#contact'? 'active': ''}><BiMessageSquareDetail/></a>
</nav>
)
}