I'm trying to implement class-swapping site navigation using React Functional components during the scroll screen.
I added a listener to windows and I'm using the getBoundingClientRect method to check the moment my top arrives in a position to change its class.
The code below always returns the Top equal to zero, regardless of the position of my scroll.
Where am I going wrong in this example?
import React, {useEffect, useRef} from "react";
const Navigation = () => {
const inputRef = useRef();
const sections = [{
name: "Portfolio",
url: "#portfolio"
},
{
name: "About",
url: "#about"
},
{
name: "Contact",
url: "#contact"
}];
const navLinks = sections.map((section, index) => {
return (
<li className="nav-item mx-0 mx-lg-1" key={index}>
<a className="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href={section.url}>{section.name}</a>
</li>
)
});
const handleScroll = () => {
let offsetTop = inputRef.current.getBoundingClientRect().top;
console.log('Top ' + offsetTop);
};
useEffect(() => {
window.addEventListener('scroll', handleScroll);
});
return (
<nav className="navbar navbar-expand-lg bg-secondary text-uppercase fixed-top" id="mainNav" ref={ inputRef }>
<div className="container">
<a className="navbar-brand js-scroll-trigger" href="#page-top">Start Bootstrap</a>
<button className="navbar-toggler navbar-toggler-right text-uppercase font-weight-bold bg-primary text-white rounded" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i className="fas fa-bars"></i>
</button>
<div className="collapse navbar-collapse" id="navbarResponsive">
<ul className="navbar-nav ml-auto">
{navLinks}
</ul>
</div>
</div>
</nav>
);
};
export default Navigation;
When scrolling the top, right, bottom, left, heigh and width positions are the same.
I expect that the top value changes when scrolling the page.
My code is working now, follow the final version
import React, { useEffect, useRef, useState } from "react";
import { Link, animateScroll as scroll } from "react-scroll";
const Navigation = () => {
const inputRef = useRef();
const [navClass, setNavClass] = useState('');
const sections = [{
name: "Portfolio",
url: "portfolio"
},
{
name: "About",
url: "about"
},
{
name: "Contact",
url: "contact"
}];
const navLinks = sections.map((section, index) => {
return (
<li className="nav-item mx-0 mx-lg-1" key={index}>
<Link
activeClass="active"
to={section.url}
spy={true}
smooth="easeInOutQuart"
offset={-70}
duration={800}
className="nav-link py-3 px-0 px-lg-3 rounded"
href="">
{section.name}
</Link>
</li>
)
});
const scrollToTop = () => {
scroll.scrollToTop();
};
const handleScroll = () => {
let offsetTop = window.pageYOffset;
if ( offsetTop > 100 ){
setNavClass('navbar-shrink');
}else{
setNavClass('');
}
};
useEffect(() => {
window.addEventListener('scroll', handleScroll);
});
return (
<nav className={`navbar navbar-expand-lg bg-secondary text-uppercase fixed-top ${navClass}`} id="mainNav" ref={inputRef}>
<div className="container">
<a className="navbar-brand js-scroll-trigger" href="#page-top" onClick={scrollToTop}>Start Bootstrap</a>
<button className="navbar-toggler navbar-toggler-right text-uppercase font-weight-bold bg-primary text-white rounded" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i className="fas fa-bars"></i>
</button>
<div className="collapse navbar-collapse" id="navbarResponsive">
<ul className="navbar-nav ml-auto">
{navLinks}
</ul>
</div>
</div>
</nav>
);
};
export default Navigation;
If your objective is to change the class of your navbar based on the scroll position of your window, it would make more sense to use window.pageYOffset
instead. Consider that the navbar would never actually leave its position, so whenever you call .getBoundingClientRect().top
, it will always be 0.
const handleScroll = () => {
let offsetTop = window.pageYOffset;
console.log('Top ' + offsetTop);
};
Check out this sandbox on how you can change the appearance of your navbar on scroll: https://codesandbox.io/s/navbar-change-color-onscroll-jepyc