As the title describes, I am currently working on a multipage react app and I would like for users to be able to scroll from page to page as well as use a navmenu to go through the site. This way if a user wants to navigate to a specific page they can use the menu but they can also go through the site page by page. I would like a prompt to come up when the user arrives at the end of a page stating to "scroll to learn more" and then if a separate scrolling event is detected, then the app navigates to the next page.
I currently have something that functions but not as I would like it to which is the following code:
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { FaArrowDown } from "react-icons/fa";
import classes from "./ScrollingNav.module.css";
function ScrollingNav({ pages, children }) {
const navigate = useNavigate();
const [showPrompt, setShowPrompt] = useState(false);
const [atBottom, setAtBottom] = useState(false);
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
const handleScroll = () => {
const windowHeight = window.innerHeight;
const scrollPosition = window.scrollY;
const contentHeight = document.documentElement.scrollHeight;
const scrollBuffer = 100;
// const minDistance = 200;
if (
contentHeight - (scrollPosition + windowHeight) < scrollBuffer &&
// contentHeight- (scrollPosition + windowHeight) > minDistance
!atBottom
) {
setShowPrompt(true);
} else {
setShowPrompt(false);
}
if (contentHeight - (scrollPosition + windowHeight) === 0) {
setAtBottom(true);
}
};
const handleNavigate = () => {
if (atBottom) {
if (currentIndex + 1 < pages.length) {
setCurrentIndex(currentIndex + 1);
navigate(pages[currentIndex + 1]);
}
}
};
window.addEventListener("scroll", handleScroll);
window.addEventListener("scroll", handleNavigate);
return () => {
window.removeEventListener("scroll", handleScroll);
window.removeEventListener("scroll", handleNavigate);
};
}, [currentIndex, navigate, pages, atBottom]);
return (
<div>
{children}
{showPrompt && (
<div>
Scroll to Learn More
<FaArrowDown size={50} />
</div>
)}
</div>
);
}
export default ScrollingNav;
I have the component which receives a list of routes and a children prop. The list of pages is for indexing which page is next. The current code renders the prompt when the user arrives at the end of the page but only navigates if the user scrolls upwards from the bottom. I believe this to be due to the if statement:
if (contentHeight - (scrollPosition + windowHeight) === 0) {
setAtBottom(true);
}
The issue is if I set the value being compared with the anything other than 0, for example:
if (contentHeight - (scrollPosition + windowHeight) < 5) {
setAtBottom(true);
}
then the app instantly scrolls when the user reaches the bottom and the footer is not even completely visible yet. The header and footer and rendered outside of the page content in app.js so that they are present at the top and bottom of every page like this:
function App() {
return (
<UserContextProvider>
<div className="App">
<Header />
<Routes>
<Route path="/" element={<HomePage />} /}
... other routes
</Routes>
<Footer />
</div>
</UserContextProvider>
);
}
I am not sure if the pages being set up like this is causing any issues so I figured I would include this for context.
There could be an entirely different way to go about achieving my desired outcome so I am open to any suggestions. Is there a way to modify my code such that it isolates a separate downward scrolling event after the bottom of the page is reached which causes the app to navigate to the next page?