DEV Community

Cover image for Create animated navigation indicator with HTML, CSS and JS
Adrian Bece
Adrian Bece

Posted on • Updated on

Create animated navigation indicator with HTML, CSS and JS

I've never had the chance to use this kind of menu on my regular projects, so I decided to make one, just for fun. I was really happy with the final result, so I decided to share it with the community. Even though I created this one using React, I wanted to make one with vanilla JavaScript so you can use it on any project.

HTML setup

Let's start off by creating a simple navigation markup with a simple list with a few links. We'll add id attributes to the root nav element and a div element that we'll use as a pointer. We'll need a few classes for the nav, list, and pointer elements so we can style them.



<nav class="nav" id="js-nav">
  <div id="js-pointer" class="nav__pointer"></div>
  <ul class="nav__list">
    <li><a href="#">Overview</a></li>
    <li><a href="#">Goals</a></li>
    <li><a href="#">Inspiration</a></li>
    <li><a href="#">Profile</a></li>
  </ul>
</nav>


Enter fullscreen mode Exit fullscreen mode

CSS markup

Let's add some styles. The following code snippet shows only required styles.

We need to position nav element relatively and add some padding. We'll need that value for JavaScript. We'll position nav__pointer absolutely with a z-index value that is lower than nav__list so the indicator is positioned under the links.

We need to position the links in a 4 * 1fr column grid so all link containers are equal width.



.nav {
  position: relative;
  padding: 1em;
}

.nav__pointer {
  z-index: 1;
  position: absolute;
  top: 0.6em;
  left: 1em;
  background-color: #bada55;
  height: 1.8em;
  transition: transform 0.25s ease-in-out;
  border-radius: 0.3em;
  will-change: transform;
  backface-visibility: hidden;
}

.nav__list {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}



Enter fullscreen mode Exit fullscreen mode

JavaScript

We need to select our navigation, indicator, and link elements with JavaScript. Remember that 1em value padding from CSS? We'll use half of that value so we can position the pointer appropriately.

To avoid using magic numbers, we'll dynamically calculate the indicator's width depending on the number of columns in the grid (how many links there are).



var CONTAINER_PADDING_HALF = "0.5em";

pointer.style.width = "calc(100% /"+links.length+" - "+CONTAINER_PADDING_HALF+")"


Enter fullscreen mode Exit fullscreen mode

For each selected link within the nav element, we'll add a data attribute that stores a percentage value based on the order. If the link is first in the list, it will have a 0% value, if it's a second, it will have a 100% value, etc. We'll use those values for transforms.

We're also attaching a click event listener for each link.



for(var i=0; i<links.length; i++){
  var current = links[i];
  current.dataset.order = i * 100 + "%";  
  current.addEventListener("click", movePointer);
}


Enter fullscreen mode Exit fullscreen mode

Our link click event handler is very simple - it only applies a CSS transform attribute to the navigation indicator. The value that is being applied depends on data-order attribute that we've set.



function movePointer(e) {
  var order = e.currentTarget.dataset.order;
  pointer.style.transform = "translate3d("+order+",0,0)"
}


Enter fullscreen mode Exit fullscreen mode

Since the width of the indicator matches the width of the each navigation link in the grid, and we've positioned the indicator absolutely and to the start of the first link, we only have to apply transforms in 100% increments. First link will have the 0% value, second link will have the 100% value, third link will have the 200% value, etc.

By using 3D CSS transforms, this animation will be GPU-powered and it will be smooth and performant.

Final Result

Here is the CodePen link to the final result.


These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.

Buy Me A Coffee

Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.

Top comments (1)

Collapse
 
dinhtheloc profile image
Dinh The Loc

❤️ 🦄