0

I have been trying to make the links in my sticky nav bar highlight when on the corresponding section.

I have been able to get it to partially work using JavaScript with all links being highlighted when scrolling over the corresponding sections, however, the section 1 link is highlighted even if I'm on the landing page and have not scrolled at all. How can I make it so that section 1 isn't highlighted until I scroll to the section?

I've tried to use an if statement to set all links to not active if window.scrollY === 0, but this only works for the initial render and doesn't work after I have scrolled down and back.

Update

Adding another if statement at the start of the highlightNav function to check if window.scrollY === 0 has allowed me to remove the .active class from all the links, but ideally I want it to work when window.scrollY < 450 which doesn't seem to work.

My JavaScript is as follows:

const highlightNav = () => {


    const links = document.querySelectorAll('.link');
    const section = document.querySelectorAll('.section');

    //updated if statement
    //I tried using window.scrollY < 450 in the if statement below with no success

    if (window.scrollY === 0) {
        links.forEach((link) => link.classList.remove('active'));
    };

    if (window.scrollY !== 0) {

        const changeLinkState = () => {
            let index = section.length;

            while(--index && window.scrollY + 450 < section[index].offsetTop) {};

            links.forEach((link) => link.classList.remove('active'));
            links[index].classList.add('active');

        };
    changeLinkState();
    };
};

highlightNav();
window.addEventListener('scroll', highlightNav);

The HTML follows this structure:

<header>
    <div class="nav-bar">
        <nav>
            <a href="#about" class="link">About</a>
            <a href="#price" class="link">Pricing</a>
            <a href="#contact" class="link">Contact</a>
            <a href="">Sign in</a>
        </nav>
    </div>
    <section>

        <!-- Landing Screen -->

    </section>
</header>

<article>

    <section id="about" class="section">

       <!-- section 1 -->

    </section>

    <section id="price" class="section">

           <!-- Section two  -->

    </section>

    <section id="contact" class="section">

            <!-- Section Three -->

    </section>
</article>

css:

.nav-bar {
    position: fixed;
    top: 0px;
    width: 100%;
    height: 8vh;
    display: flex;
    justify-content: space-between;
    align-items: center;
    background-color: #297094;
    text-align: center;
    box-shadow: 0 3px 3px rgba(0, 0, 0, 0.3);
}

nav {
    width: 25%;
    height: 100%;
    display: flex;
    justify-content: flex-end;
    align-items: center;
}

nav a {
    padding: 0 10%;
    border-left: 1px solid black;
    height: 100%;
    display: flex;
    align-items: center;
    font-size: 1.25rem;
    color: white;
    transition: background-color 600ms;
    text-decoration: none;
    white-space: nowrap;
}

nav a:hover {
    background-color: #3B80A0;
    cursor: pointer;
}

.active {
    background-color: #3B80A0;
}

I'm very new to JavaScript so any help is welcome, thanks in advance.

James
  • 11
  • 3
  • Can you share the css? – Samuel May 18 '20 at 19:37
  • I have added the CSS for the nav bar. – James May 18 '20 at 19:44
  • I cannot see a sticky nav.. Any there is an empty body for your while statement – Samuel May 18 '20 at 19:49
  • Sorry I missed a bit of the CSS which, I've now added. Yeah, I don't fully understand the logic of the while statement yet as I've had to try and work out the code backward. I used the following thread to help me in the beginning: (https://stackoverflow.com/questions/52025615/vanilla-js-change-active-state-of-links-when-scrolling-refactoring) – James May 18 '20 at 20:21

1 Answers1

1

Main issue:

  • Instead of mixing that 450 math into a while loop, define that amount as a constant with a descriptive name, then use it in your conditional.

A couple other notes:

  • The while loop in your posted code doesn't do anything, since there's nothing inside the {}
  • You call links.forEach((link) => link.classList.remove('active')); twice, so make that its own function (I've named it clearAllActive() here).
const highlightNav = () => {
  const links = document.querySelectorAll('.link');
  const sections = document.querySelectorAll('.section');

  const clearAllActive = () => links.forEach(link => link.classList.remove('active'));

  const highlightActive = () => {

    // your highlighting code here

  };

  // don't start highlighting until we've scrolled at least this far
  const minScrollBeforeHighlighting = 450;

  if (window.scrollY < minScrollBeforeHighlighting) {
    clearAllActive();
  } else {      
    highlightActive();
  };
};

highlightNav();
window.addEventListener('scroll', highlightNav);
simmer
  • 2,639
  • 1
  • 18
  • 22
  • 1
    Thanks so much for the help! I've had a quick test with your code and it did correctly wait till I scrolled the 450, however, It highlighted all the links and then made each link inactive as soon as the top of the corresponding section left the viewport. I then tried your code with my `changeLinkState()` instead of `highlightActive()` and it works perfectly. – James May 18 '20 at 21:35
  • Excellent! I've updated my example code above to simply highlight the part that worked and to correct the big-arrow syntax on `clearAllActive()`. – simmer May 18 '20 at 22:07