Intersection Observer - Add class to logo if specific section is in viewport?

I will try to explain what I am trying to do here. I have created 5 sections and each has a min-height of 100vh to create a fullscreen effect when you scroll down. Each section has a unique ID.

Now, I have a fixed CSS navbar that is set to the top. Inside the navbar, I have a logo and I would like to add a class to the logo IF the IO detects if a specific section with a unique ID is in the viewport.

The problem I am facing is that the IO will work with a threshold set to 0.99 or 1.
Immediately after the FIXED navbar enters another section from the top the class should apply.

This works… but I also want to remove the same class if you scroll back like to the previous section.

The problem is… the class will only be removed if you reach the TOP of the previous section and not from the bottom.

Depending on if you scroll from top or from bottom to the top. The IO really confuses me. Also if you scroll too fast will cause this not to work.

The thing that I am trying to do is that I have 2 sections with a white background and 2 with a pink background.

The logo has a white text color but should be for instance black if you enter the white section and if you go back to white it should be pink. If that make sense? Something like a scroll spy but only the logo should have a class to it.

My code:

section {
 min-height: 100vh;
 position: relative;
 display: flex;
 align-items: center;
 justify-items: center;
#intro { background: cyan; }
#about { background: magenta; }
#services { background: yellow; }
#contact { background: lime; }

.fixed-bar {
  top: 0;
  position: fixed;
  width: 100%;
  height: 4em;
  background: #222;
  z-index: 1;  
<div class="fixed-bar">
  <h2 class="navigation__brand">MY LOGO</h2>

<section id="diensten">Content</section>
<section id="werkwijze">Content</section>
<section id="team">Content</section>
<section id="contact">Content</section>
(() => {
    const sections = document.querySelectorAll('section');
    const navBrand = document.querySelector('.navigation__brand');

    const changeNav = (entries) => { = '0';

        entries.forEach((entry) => {
            const id ='id');
            const currentY = entry.boundingClientRect.y;
            const currentRatio = entry.intersectionRatio;
            const isIntersecting = entry.isIntersecting;

            if (currentY <= 0) {
                if (isIntersecting && currentRatio) {
                    if (id === 'team') {
               = '2px solid red';

                    if (id === 'diensten') {
               = '2px solid green';

    const options = {
        threshold: [0.01, 0.99],
        rootMargin: '0px 100px 0px 0px'

    const observer = new IntersectionObserver(changeNav, options);

    sections.forEach((section) => {

Hopefully, someone can help me out. Thanks in advance.

What is rendering .navigation__brand? I don’t see that anywhere in the html nor do I see any calls to it before your changeNav

script.js:6 Uncaught TypeError: Cannot read property 'style' of null
    at IntersectionObserver.changeNav (script.js:6)

Also there’s a check for team and diensten but no where in the html that exist. Are those supposed to be rendered by the script file?


I have updated my code.

1 Like

I think the problem here is that we’re observing the event the moment the transition stops. Because we’re only triggering our changeNav when there is an intersection and the transition stops, we’ll never have a precise ratio. Hence, despite scrolling back to the top, currentY will never achieve true zero.

My only suggestion is creating an onscroll event which checks to see if we’re at the top of the page after the user scrolls to the top. I know, hacky at best but this all I can think of :\

    window.addEventListener("scroll", function(e) {
        if(window.scrollY == 0) {
   = '0';

I’m sure there is a more elegant, correct way to resolve this

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.