Search code examples
javascriptfrontendnavbarnavonscroll

change the active list when scrolling navbar


I'am trying to makes my navbar moves between sections will I scroll down.

I tried to makes it with JS but I cant makes it work it is change the active class when I click with event listener but I can't make it work when I scroll

Thanks for any help

here is my js code

let section = document.querySelectorAll('section')
let lists = document.querySelectorAll('.list');
function activeLink(){
    lists.forEach((item) =>
    item.classList.remove('active'));
    this.classList.add('active');
}
lists.forEach((item) =>
item.addEventListener('click',activeLink));

window.onscroll = () =>{
  section.forEach(sec =>{
    let top = window.scrollY;
    let offset = sec.offsetTop;
    let height = sec.offsetHeight;
    let id = sec.getAttribute('id');

    if(top >= offset && top < offset + height){
      lists.forEach(sec =>{
        activeLink;
      })
    }
  })
};

and this is my html

<nav class="navigation">
        <ul>
            <li class="list active">
                <a href="#home">
                    <span class="icon">
                        <ion-icon name="home-outline"></ion-icon>
                    </span>
                    <span class="title">Home</span>
                </a>
            </li>
            <li class="list">
                <a href="#about">
                    <span class="icon">
                        <ion-icon name="person-outline"></ion-icon>
                    </span>
                    <span class="title">About Me</span>
                </a>
            </li>
            <li class="list">
                <a href="#working">
                    <span class="icon">
                        <ion-icon name="newspaper-outline"></ion-icon>
                    </span>
                    <span class="title">Working</span>
                </a>
            </li>
            <li class="list">
                <a href="#">
                    <span class="icon">
                        <ion-icon name="code-slash-outline"></ion-icon>
                    </span>
                    <span class="title">Learned</span>
                </a>
            </li>
            <li class="list">
                <a href="#contact">
                    <span class="icon">
                        <ion-icon name="chatbox-outline"></ion-icon>
                    </span>
                    <span class="title">Contact</span>
                </a>
            </li>
        </ul>
    </nav>

Solution

  • The first problem with your code is that you're not calling the "activeLink" function correctly.

     if(top >= offset && top < offset + height){
      lists.forEach(sec =>{
        activeLink;
      })
    }
    

    If you add the parentheses, then it'll work and throw an error due to the "this" keyword which refers to the window object in this case.

    To work, in the "onScroll" handler, you can replace your code with this piece:

    if (top >= offset && top < offset + height) {
        lists.forEach((item) => {
            item.classList.remove('active');
            if(item.querySelector('a').getAttribute('href') == '#' + id){
                item.classList.add('active');
            }
         });            
    }
    

    Of course, your aim will be not to duplicate your code. So, we can first select the target like that:

    const target = document.querySelector(`[href='#${id}']`).parentElement;
    activeLink(target);
    

    Passing it to the "activeLink" function directly will lead you to check the coming parameter if it is the "click event" or the list item and handles both cases. instead, you can edit your handler code like this:

    lists.forEach((item) =>
      item.addEventListener('click', function(){
         activeLink(this);
      }));
    

    And this is the "activeLink" function code in the final state:

    function activeLink(li) {
        lists.forEach((item) => item.classList.remove('active'));
        li.classList.add('active');
    }
    

    Here is the code snippet for this:

    let section = document.querySelectorAll('section');
            let lists = document.querySelectorAll('.list');
            function activeLink(li) {
                lists.forEach((item) => item.classList.remove('active'));
                li.classList.add('active');
            }
            lists.forEach((item) =>
                item.addEventListener('click', function(){
                    activeLink(this);
                }));
    
            window.onscroll = () => {
                section.forEach(sec => {
                    let top = window.scrollY;
                    let offset = sec.offsetTop;
                    let height = sec.offsetHeight;
                    let id = sec.getAttribute('id');
    
                    if (top >= offset && top < offset + height) {
                        const target = document.querySelector(`[href='#${id}']`).parentElement;
                        activeLink(target);
                    }
                })
            };
    .sec {
                height: 500px;
            }
    
            nav {
                position: fixed;
                width: 100%;
                top: 0;
                left: 0;
                z-index: 10;
                background-color: #fff;
            }
    
            .active a {
                color: palevioletred;
            }
    <nav class="navigation">
            <ul>
                <li class="list active">
                    <a href="#home">
                        <span class="icon">
                            <ion-icon name="home-outline"></ion-icon>
                        </span>
                        <span class="title">Home</span>
                    </a>
                </li>
                <li class="list">
                    <a href="#about">
                        <span class="icon">
                            <ion-icon name="person-outline"></ion-icon>
                        </span>
                        <span class="title">About Me</span>
                    </a>
                </li>
                <li class="list">
                    <a href="#working">
                        <span class="icon">
                            <ion-icon name="newspaper-outline"></ion-icon>
                        </span>
                        <span class="title">Working</span>
                    </a>
                </li>
                <li class="list">
                    <a href="#Learned">
                        <span class="icon">
                            <ion-icon name="code-slash-outline"></ion-icon>
                        </span>
                        <span class="title">Learned</span>
                    </a>
                </li>
                <li class="list">
                    <a href="#contact">
                        <span class="icon">
                            <ion-icon name="chatbox-outline"></ion-icon>
                        </span>
                        <span class="title">Contact</span>
                    </a>
                </li>
            </ul>
        </nav>
    
        <section class="sec" style="background-color: beige;" id="home"></section>
        <section class="sec" style="background-color: yellow;" id="about"></section>
        <section class="sec" style="background-color: orange;" id="working"></section>
        <section class="sec" style="background-color: orangered;" id="Learned"></section>
        <section class="sec" style="background-color: olive;" id="contact"></section>