Introduction
Have you ever hovered over a navigation link on a website and noticed that all the other links fade out, leaving only the one you're hovering over? In this tutorial, you will create that effect using just a few lines of JavaScript.
Here's a demo of the effect we're going to create:
The HTML
To create a basic navigation, we have a div
with the class nav
, containing a ul
element with the class nav-list
. Within the ul
, there are five li
elements, each with the class nav-link
.
<div class="nav">
<ul class="nav-list">
<li class="nav-link">Home</li>
<li class="nav-link">Features</li>
<li class="nav-link">Operations</li>
<li class="nav-link">Testimonials</li>
<li class="nav-link">Contact</li>
</ul>
</div>
Styling the navigation
We’ll add some visual styles to the nav
.nav {
height: 7rem;
background-color: pink;
padding: 2rem;
}
.nav-list {
display: flex;
gap: 3rem;
justify-content: center;
align-items: center;
list-style: none;
}
.nav-link {
transition: all 0.3s;
font-size: 2.5rem;
cursor: pointer;
}
Output:
The JavaScript
The main properties we need to implement this menu fade effect are:
- The mouseover event: this event is triggered when a mouse cursor hovers over an element.
- The mouseout event: this is the opposite of the mouseover event. It happens when a mouse cursor moves away from an element.
- Opacity: this property will give us the fade-in/fade-out effect.
We’ll start by attaching an event Listener to the common parent of the li
, instead of attaching it to each element separately. This method is called event delegation. In case you’re unfamiliar with event delegation, read my article on it here.
let’s select all the elements we’ll use and save them into variables. We’ll use these variables throughout this tutorial.
const nav = document.querySelector(".nav");
const navList = document.querySelector(".nav-list");
const navLink = document.querySelectorAll(".nav-link");
Next step is to add the event listener:
nav.addEventListener("mouseover", function (e) {
console.log(e.target);
});
You may need clarification here. In the HTML, the li
has 2 parents; the ul
and the div
. You can add the event listener to either of them. I chose to use the div
as our parent element. Hence, the event listener was attached to it.
We’re only interested in the event that happens on the li
, not on the ul
or div
. The event object (e)
has a target
property that helps us identify where an event originated from. This event.target (e.target)
is the element where an event happened. In this case, e.target
is the element you’re hovering over.
So now If you hover over any of the parent elements, the console will log information about the parent. If you hover over the li
, the console will log information about the li
. We’ll use this information to isolate only the event coming from the li
. Since the li
has the class nav-link
, we’ll first check if any element we’re hovering over contains this class before adding any functionality.
nav.addEventListener("mouseover", function (e) {
if (e.target.classList.contains('nav-link')){
console.log(e.target);
}
});
Now if you hover over the ul
or the div
, nothing will appear in the console because they do not contain the nav-link
class. If you hover over the li
, it will appear in the console because it contains the class.
The next step is to find the sibling elements of the e.target
. We’ll do this by selecting the parent nav
and then we’ll select all the li
children of the nav
.
if (e.target.classList.contains("nav-link")) {
const siblings = e.target.closest(".nav").querySelectorAll(".nav-link");
}
We used the closest()
method to find the nearest parent of the e.target
that has the class nav
. Then we chained querySelectorAll()
to select all its child elements that have the class nav-link
. This will give us a node list containing all the li
elements. That is the e.target
and all its siblings. We’re going to loop over this node list to reduce the opacity of all the sibling elements of the e.target
.
nav.addEventListener("mouseover", function (e) {
if (e.target.classList.contains("nav-link")) {
const siblings = e.target.closest(".nav").querySelectorAll(".nav-link");
siblings.forEach((el) => {
if (el !== e.target) {
el.style.opacity = "0.5";
}
});
}
});
This code checks if the current element is not e.target
(i.e., the element you're hovering over). If it's not, it reduces the element's opacity to 0.5
. As a result, when you hover over any navigation link, all other links will fade out, leaving only the hovered link fully visible.
Notice that the links didn’t return to normal when the cursor moved away. This is where the mouseout event comes in handy. Remember, the mouseout event fires off when the cursor moves away from an element. So we’ll attach the mouseout event listener to the nav
. The code will be the same as the mouseover event listener except for the opacity. The opacity here will be set to 1
so that the elements will return to their normal state when the cursor moves away.
We’ve successfully implemented the menu fade animation. When you hover over any of the links, the others fade out and if you move the cursor away, they go back to normal (opacity goes back to 1).
Refactoring the code
I’m sure you’ve noticed that we have duplicate code. The code in both the event listeners is the same except for the opacity values. This goes against the D.R.Y (don't repeat yourself) principle. Let’s refactor the code into a function. We’ll call the function inside each event listener and then pass in the event (e) and the opacity as arguments.
How do we call this function in the event listeners? Usually, when we have a function like this, it is passed into the event listener as a callback function.
nav.addEventListener("mouseout", navHover);
The issue here is we want to pass in the event (e) and opacity into this function. Ideally, arguments cannot be passed directly to event listeners. This will always return an error. How do we tell this function to use an opacity of 0.5
in the mouseover event and an opacity of 1
in the mouseout event? The solution would be to still have a regular callback function in the event listener, which JavaScript will call whenever the event happens. Inside this callback, we can then call the navHover
function manually and pass in the arguments.
nav.addEventListener("mouseover", function (e) {
navHover(e, 0.5);
});
nav.addEventListener("mouseout", function (e) {
navHover(e, 1);
});
This function will be executed as soon as JavaScript executes the callback function.
Conclusion
In addition to learning how to make the cool menu fade animation with a few lines of JavaScript; we’ve also learned how to pass in a function with arguments into an event Listener.
Top comments (2)
Great tutorial! Love how simple and effective this method is for creating a sleek fade effect. Thanks for sharing!
Thank you for the feedback