Search code examples
javascripteventsevent-handlingnavbarparent-child

How to make navbar and its elements change based on hover and scrollY?


Hi I'm making a navbar that changes based on hover and scrollY. Here are the requirements:

when scrollY > 0 (That means, navbar not on top):
    navbar bg-color -> "#fff"    
    children (a, i, p tags) color -> "#000"    
    when child hovered:
       child color -> "ff7bac" 
and that is whiteNavbar() function, it is done and it's working. now I need to do: 
when scrollY <= 0 (navbar on top):
    navbar bg-color -> rgba(0, 0, 0, 0.2)
    children color -> "#fff"
    when navbar hovered:
        navbar bg-color -> "#fff"
        children color -> "#000"
    when child hovered:
        child color -> "ff7bac"

when navbar and child hovered when nothing hovered and scrollY <= 0 when navbar hovered and scrollY <= 0 OR when scrollY > 0

JS code:

let navbar = document.querySelector('.navbar');
let navbar_tags = navbar.querySelectorAll("p, i, a");
let navbar_products = document.querySelector('.navbar-products');
let children = [...navbar_tags, navbar_products];
let timeout_n;

function whiteNavbar() { 
    navbar.style.backgroundColor = '#fff';
    children.forEach(child => {
        child.style.color = "#000";
        child.addEventListener('mouseover', function() {
            this.style.color = "#ff7bac";
        });
        child.addEventListener('mouseout', function() {
            this.style.color = "#000";
        });
    });
}

function transparentNavbar() {
    
}

function changeNavbarColor() {
    const scrollY = window.scrollY;
    if (scrollY > 0) {
        whiteNavbar();
    }
    else {
        transparentNavbar();
    }
}


transparentNavbar();
window.addEventListener('scroll', function () {
    clearTimeout(timeout_n);
    timeout_n = setTimeout(changeNavbarColor, 10);
});

At the beginning I had it done in CSS, but I had to switch to JS to add functionality when scrollY > 0 When doing it similarly to whiteNavbar() function everything works, besides that navbar event is overlapping a child event, and child color -> "ff7bac" stops working. code:

function transparentNavbar() {
    navbar.style.backgroundColor = 'rgba(0, 0, 0, 0.2)';
    children.forEach(child => {
        child.style.color = "#fff";
        child.addEventListener('mouseover', function() {
            this.style.color = "#ff7bac";
        });
        child.addEventListener('mouseout', function() {
            this.style.color = "#fff";
        });
    });

    navbar.addEventListener('mouseover', function() {
        this.style.backgroundColor = '#fff';
        children.forEach(child => {
            child.style.color = "#000";
        });
    });

    navbar.addEventListener('mouseout', function() {
        this.style.backgroundColor = 'rgba(0, 0, 0, 0.2)';
        children.forEach(child => {
            child.style.color = "#fff";
        });
    });
}

This is the best version ^^, almost works besides child color -> "#77bac" when hovered

I tried stopPropagation() but then navbar hover bg-color change, stopped working.


Solution

  • I think you can simplify this logic with Css instead of relying completely on JS events. I would simply create a class and that class will be responsible for setting the styles for the navbar background, and child links.

    As you have not provided the complete code, I have added a position sticky and top: 0 to show the complete working example. Here is how you can do it.

    let timeout_n; // Define timeout_n variable
    
    const navbar = document.querySelector('.navbar');
    const navbarTags = navbar.querySelectorAll("p, i, a");
    
    function whiteNavbar() {
        navbar.classList.add('white-bg');
        navbarTags.forEach(child => {
            child.classList.add('white-text');
        });
    }
    
    function transparentNavbar() {
        navbar.classList.remove('white-bg');
        navbarTags.forEach(child => {
            child.classList.remove('white-text');
        });
    }
    
    function changeNavbarColor() {
        const scrollY = window.scrollY;
        if (scrollY > 0) {
            whiteNavbar();
        } else {
            transparentNavbar();
        }
    }
    
    transparentNavbar();
    
    window.addEventListener('scroll', function() {
        clearTimeout(timeout_n);
        timeout_n = setTimeout(changeNavbarColor, 10);
    });
    /* Initial styles for navbar */
    .navbar {
        top:0;
        position: sticky;
        background-color: rgba(0, 0, 0, 0.2);
        color: #fff;
        border: 1px solid;
    }
    
    /* Styles when navbar is white */
    .navbar.white-bg {
        background-color: #fff;
        color: #000;
    }
    
    /* Styles for child elements when navbar is white */
    .navbar.white-bg p,
    .navbar.white-bg i,
    .navbar.white-bg a {
        color: #000;
    }
    
    /* Hover styles for child elements when navbar is white */
    .navbar p:hover, .navbar.white-bg p:hover,
    .navbar i:hover, .navbar.white-bg i:hover,
    .navbar a:hover, .navbar.white-bg a:hover {
        color: #ff7bac;
    }
    
    /* Hover styles for navbar when navbar is white */
    .navbar.white-bg:hover {
        background-color: #fff;
    }
    <div class="navbar">
      <a href="#">Link 1</a>
      <a href="#">Link 2</a>
      <i>Icon</i>
      <p>Paragraph</p>
    </div>
    
    <br/>
    <br/>
    <br/>
    <br/>
    <br/>
    
    <br/>
    <br/>
    <br/>
    <br/>
    <br/>
    
    <br/>
    <br/>
    <br/>
    <br/>
    <br/>
    
    <br/>
    <br/>
    <br/>
    <br/>
    <br/>