I have a SPA with a long height page, where one section represents one page. Actual code:
const ScrollWrapper = () => {
return (
<div className={styles.ScrollWrapper + ` ` + 'snap-container'}>
<div className={styles['navigation-fix-wrapper']}>
<ScrollNavigation/>
</div>
<Home/>
<Projects/>
<Media/>
<Concerts/>
<Contacts/>
</div>
)};
A have a navigation component at the top. It's a bunch of anchors with list items inside it, every anchor links to a sections like with scrolling effect, no page changes. On click styling of an clicked link changes to active.
Goal: implement IntersectionObserver to all of my sections (from Home to Contacts) and depending on which component is in viewport the active link in ScrollNavigation component will be changed (if I scroll from Home to Concerts, the Concerts link will be active)
I have absolute zero ideas how can I do it, but I'm pretty sure that there are some ways.
I think of something like (pseudocode):
const [activeLink, setActiveLink] = useState('home')
//some code here
if (IntersectionObserver.state === 'home' {
setActiveLink('home')
} else if (IntersectionObserver.state === 'concerts' {
setActiveLink('concerts')
}
//some code here
return (
<div className={styles.ScrollWrapper + ` ` + 'snap-container'}>
<ScrollNavigation active={activeLink} />
//some code here
</div>
)
UPDATE:
Added this code to ScrollWrapper, as kritiz suggested. IntersectionObserver fires console.log only if parent component is in if condition, but not the children. What I'm doing wrong?
useEffect(() => {
var observer = new IntersectionObserver(onIntersection, {
root: null, // default is the viewport
threshold: .5 // percentage of taregt's visible area. Triggers "onIntersection"
})
// callback is called on intersection change
function onIntersection(entries, opts){
entries.forEach(entry => {
if (entry.target.id === 'projects') {
console.log('projects')
}
}
)
}
observer.observe( document.querySelector('.snap-container') )
}, [])
Changed target to currentTarget, error occures:
Cannot read properties of undefined (reading id)
As kritiz suggested, I used a scroll event listener on window and it actually worked. It still sounds like memory leaks, and I will continue to try implementing the IntersectionObserver, but it works as I needed.
Code:
useEffect(() => {
window.addEventListener('scroll', function() {
const observable = {
home: document.querySelector('.home'),
projects: document.querySelector('.projects'),
media: document.querySelector('.media')
}
const pos = {
home: observable.home.getBoundingClientRect(),
projects: observable.projects.getBoundingClientRect(),
media: observable.media.getBoundingClientRect()
}
if (pos.home.top < window.innerHeight && pos.home.bottom >= 0) {
setActiveLink('home')
} else if (pos.projects.top < window.innerHeight && pos.projects.bottom >= 0) {
setActiveLink('projects')
} else if (pos.media.top < window.innerHeight && pos.media.bottom >= 0) {
setActiveLink('media')
}
})
return () => {
}
}, [])
UPDATE:
Second approach is to use react-scroll library, which works like a charm and does everything I need.