Active Navigation Bar Scroll

Hi! I need help with creating a navigation bar where when the user scrolls on the current menu item then the anchor link should change color as an indication. Online, all I see is JQuery. I am looking for vanilla JS, thanks! :wink:

So you need to:

  1. select all anchors
  2. get their offset from the top of the page
  3. add listener on scroll event
  4. select all links to those anchors
  5. whenever scroll top value reaches the anchor offset you change/toggle the state of corresponding links

Everything except no. 2 is pretty easy and straight forward, no. 2 you can do like this:

// assuming you've selected an anchor
// sum of the distance to the top of the window and distance from the top of the page that has been scrolled
const top = anchor.getBoundingClientRect().top + window.scrollY;

You have to note, that resize event will change that distance, so on resize distances have to be updated

I was able to get until this part:

const anchors = document.querySelectorAll(".page-link");
let top = anchors.getBoundingClientRect().top + window.scrollY;
window.addEventListener("scroll", ()=> {
 
})

What do you mean by anchors and the links to them?

There are a bit of naming confusions in HTML due to its age and <a> is one of them, because you can use it in two ways:

<a id="MySectionToLinkTo"></a>
<a href="#MySectionToLinkTo">Go to my section</a>

The first one is an ā€˜anchorā€™. In HTML5 you technically can use any element to be served as anchor, like so: <div id="MySectionToLinkTo"></div>, but originally it was a role of a element.

When you add href attribute to the anchor it dramatically changes its behavior and turns into a link. Sometimes people still call them ā€œanchorsā€ - there are no stable convention about this

1 Like

I donā€™t know exactly how to do this
I have tried multiple times, is there an example I can see? Or can someone help me?
Here is what I have done: gives me error

const anchorH = document.querySelector("#home");
const anchorA = document.querySelector("#about");
const anchorS = document.querySelector("#skills");
const anchorP = document.querySelector("#projects");
const anchorC = document.querySelector("#contact");

let anchorList = [anchorH, anchorA, anchorS, anchorP, anchorC];

window.addEventListener("scroll", () => {
  for (let anchor in anchorList) {
    if (
      anchor.getBoundingClientRect().top + window.scrollY <=
      anchor.offsetTop()
    ) {
      console.log("highlight link");
    }
  }
});

Also, is there a shorter way?

You can also try using the Intersection Observer API. Here is a Codepen with an example and here is the article the example came from. More info google search and youtube search.

2 Likes

I found another solution: unfortunately I had to reference another website. Here it is:

let mainNavLinks = document.querySelectorAll(".page-link");
let mainSections = document.querySelectorAll("main section");

window.addEventListener("scroll", event => {
  let fromTop = window.scrollY - 50;

  mainNavLinks.forEach(link => {
    let section = document.querySelector(link.hash);

    if (
      section.offsetTop <= fromTop + 55 &&
      section.offsetTop + section.offsetHeight > fromTop + 55
    ) {
      link.classList.add("current");
    } else {
      link.classList.remove("current");
    }
  });
});

What do you think?
Could someone also explain this a bit better?

No, I found it online on this website. But let me try to explain what I learned.

First, we select the navigation links (the ones we will actually style depending on the userā€™s position) and also select the places where our links will change. (So an about page, or contact page)

Next, we set up an event listener for scrolling which will execute our entire function.
In it, we first calculate the distance from the top (I added a -50 because I have a fixed navigation bar that spans 50px high)

Then, for each navigation link, we assign the, section variable, their respective href anchor (where the link directs us when we click on it) using link.hash

If we find that the distance between the section is less than the distance from the top AND the combined distances is greater than the distance from the top (to make sure we only style one link at a time) THEN we simply add a class to the link.

Sorry we the inconsistent English, wanted to just focus on the concept and not grammar. Also, it may not be explained thoroughly as I couldnā€™t understand this to the full extent.

Good stuff! Thanks for sharing :+1:

1 Like

That sounds about right. I think you understand the code just fine.

1 Like