1. Web Design
  2. JavaScript

Build a Navigation Menu With an Animated Active Indicator (JavaScript)

Scroll to top
Read Time: 4 min

In a previous tutorial, we discussed how to build a shifting underline hover effect. Today, we’ll learn how to create another fancy menu effect: each time we click on, or hover over an item (the choice is yours!) there will be a magic moving element that will follow along with the active item.

During this journey, we'll also cover a useful technique to update CSS pseudo-elements styles through JavaScript with the help of the CSS variables.

Navigation Demos

Take a look at the first demo where the animation will happen on click

Then the second one where the animation will happen on hover:

1. Begin With the HTML Markup

To develop this component, we’ll define a nav element that will contain the menu wrapped inside a container. By default, we’ll give the active class to the first list item, but we can equally assign this class to any of them.

Here’s the required structure:

1
<div class="container">
2
  <nav>
3
    <ul class="menu">
4
      <li class="active">
5
        <a href="">Home</a>
6
      </li>
7
      <li>
8
        <a href="">Information About Us</a>
9
      </li>
10
      <li>
11
        <a href="">Clients</a>
12
      </li>
13
      <li>
14
        <a href="">Contact</a>
15
      </li>
16
    </ul>
17
  </nav>
18
</div>
“Information About Us” is a weird label for a menu item, but it demonstrates the changing width of the effect.

2. Add the CSS

Happily enough, we’ll only need a few styles.

As we said, the menu will live inside a container with a maximum width of 1000px.

To create the element with the moving background that will act as an active menu indicator, we won’t use any extra HTML elements. Instead, we’ll define the ::before pseudo-element of the menu and then update its transform and width values dynamically through JavaScript.

The moving elementThe moving elementThe moving element

On screens smaller than 801px, we’ll hide the moving highlighter and just use some similar styles to indicate the active menu item.

Here are the menu styles that mainly interest us:

1
/*CUSTOM VATIABLES HERE*/
2
3
.menu {
4
  list-style: none;
5
  position: relative;
6
  display: inline-flex;
7
  background: var(--pink);
8
  padding: 10px;
9
  border-radius: 15px;
10
  box-shadow: rgba(17, 12, 46, 0.15) 0px 48px 100px 0px;
11
}
12
13
.menu::before {
14
  content: "";
15
  position: absolute;
16
  top: 10px;
17
  left: 0;
18
  transform: translateX(var(--transformJS));
19
  width: var(--widthJS);
20
  height: calc(100% - 20px);
21
  border-radius: var(--active-link-border-radius);
22
  background: var(--light-pink);
23
  box-shadow: var(--active-link-box-shadow);
24
  transition: all 0.3s linear;
25
}
26
27
.menu li a {
28
  display: inline-block;
29
  position: relative;
30
  padding: 10px 20px;
31
  font-size: 20px;
32
  font-weight: 500;
33
  z-index: 1;
34
}
35
36
.menu li:not(:last-child) {
37
  margin-right: 20px;
38
}
39
40
@media (max-width: 800px) {
41
  .menu,
42
  .menu li {
43
    display: inline-block;
44
  }
45
46
  .menu li.active a {
47
    background: var(--light-pink);
48
    border-radius: var(--active-link-border-radius);
49
    box-shadow: var(--active-link-box-shadow);
50
  }
51
52
  .menu::before {
53
    display: none;
54
  }
55
}

3. Apply the JavaScript

Now for the interesting part.

We’ll specify the doCalculations() function that will get as a parameter the active item and do these things:

  • Calculate its width and offset left position relative to the parent list. 
  • Update accordingly the transformJS and widthJS CSS variables that will in their turn set the transform and width values of the menu’s ::before pseudo-element. 

This function will run in the following cases:

  • When the DOM is ready. In such a case, the active item will be the one with the active class. By default, this will be the first one.
  • Each time we click on or hover over a menu link.
  • As we resize the browser window. This is important because remember that on smaller screens we follow a different approach.

Here’s the required JavaScript code for the on click animation:

1
const menu = document.querySelector(".menu");
2
const menuLinks = menu.querySelectorAll("a");
3
const menuLinkActive = menu.querySelector("li.active");
4
const activeClass = "active";
5
6
doCalculations(menuLinkActive);
7
8
for (const menuLink of menuLinks) {
9
  menuLink.addEventListener("click", function (e) {
10
    e.preventDefault();
11
    menu.querySelector("li.active").classList.remove(activeClass);
12
    menuLink.parentElement.classList.add(activeClass);
13
    doCalculations(menuLink);
14
  });
15
}
16
17
function doCalculations(link) {
18
  menu.style.setProperty("--transformJS", `${link.offsetLeft}px`);
19
  menu.style.setProperty("--widthJS", `${link.offsetWidth}px`);
20
}
21
22
window.addEventListener("resize", function() {
23
  const menuLinkActive = menu.querySelector("li.active");
24
  doCalculations(menuLinkActive);
25
});

For the on hover animation, we’ll replace the click event with the mouseenter one like this:

1
...
2
3
menuLink.addEventListener("mouseenter", function () {
4
  document.querySelector(".menu li.active").classList.remove(activeClass);
5
  menuLink.parentElement.classList.add(activeClass);
6
  doCalculations(menuLink);
7
});

Conclusion

Done! As quickly as this, we managed to develop a practical navigation menu animation that can occur either on click or hover. A more advanced implementation of it puts dropdowns into play and builds follow-along navigation like on Stripe’s website.

Apart from the animation itself, another thing to keep in mind is the way we used CSS variables to update the pseudo-elements styles. This is a great source of knowledge in cases you have headaches from manipulating pseudo-elements through JavaScript.

Once again, let’s recall our demos.

The first demo:

And the second one:

As always, thanks a lot for reading!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.