Navbar active class not updating on jQuery page scroll

My one-page website has a fixed navbar that I want to change its active status when scrolling down to specific div positions.

Even though I tried using jQuery, the code doesn't seem to work as intended. Here is the snippet:

// SMOOTH SCROLLING PAGES

$(document).ready(function () {
$(document).on("scroll", onScroll);

//smoothscroll
$('a[href^="#"]').on('click', function (e) {
    e.preventDefault();
    $(document).off("scroll");

    $('a').each(function () {
        $(this).removeClass('active');
    })
    $(this).addClass('active');

    var target = this.hash,
        menu = target;
    $target = $(target);
    $('html, body').stop().animate({
        'scrollTop': $target.offset().top+2
    }, 800, 'swing', function () {
        window.location.hash = target;
        $(document).on("scroll", onScroll);
    });
  });
});

function onScroll(event){
var scrollPos = $(document).scrollTop();
$('main-navigation a').each(function () {
    var currLink = $(this);
    var refElement = $(currLink.attr("href"));
    if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
        $('main-navigation ul li a').removeClass("active");
        currLink.addClass("active");
    }
    else{
        currLink.removeClass("active");
    }
  });
};

Below is the HTML structure:

<nav id="main-navigation">
  <ul>
    <li class="active"><a href="#site-main">Home</a></li>
    <li><a href="#a">A</a></li>
    <li><a href="#b">B</a></li>
    <li><a href="#c">C</a></li>
    <li><a href="#d">D</a></li>
  </ul>
</nav>
<div id="a">DIV Alpha</div>
<div id="b">DIV Bravo</div>
<div id="c">DIV Charlie</div>
<div id="d">DIV Delta</div>

Despite smooth scrolling working great, the issue arises when scrolling back up from div #d - the active state of the navbar li doesn't update accordingly.

Answer №1

Here is a fiddle I prepared for you, hopefully it meets your needs - http://jsfiddle.net/skyr9999/dpuvcj5w

<div class="content">
    <div id="site-main">DIV Home</div>
    <div id="a">DIV Alpha</div>
    <div id="b">DIV Bravo</div>
    <div id="c">DIV Charlie</div>
    <div id="d">DIV Delta</div>
</div>

<nav id="main-navigation">
    <ul>
        <li><a class="menuitem" href="#site-main">Home</a></li>
        <li><a class="menuitem" href="#a">A</a></li>
        <li><a class="menuitem" href="#b">B</a></li>
        <li><a class="menuitem" href="#c">C</a></li>
        <li><a class="menuitem" href="#d">D</a></li>
    </ul>
</nav>

js

$(document).ready(function () {

    $('a[href^="#site-main"]').addClass('active');

//smoothscroll
    $('.menuitem').on('click', function (e) {
        e.preventDefault();
        //  $(document).off("scroll");
        var athis = this;
        var target = this.hash,
                menu = target;
        $target = $(target);
        $('html, body').stop().animate({
            'scrollTop': $target.offset().top + 2
        }, 800, 'swing', function () {
            window.location.hash = target;
            $('.menuitem').removeClass('active');
            $(athis).addClass('active');

        });    
    });    

    $(window).scroll(function (event) {
        var scrollPos = $(document).scrollTop();
        if (scrollPos === 0)
        {
            $('a[href^="#site-main"]').addClass('active');
            return;
        }    
        $('.menuitem').each(function () {
            var currLink = $(this);
            var refElement = $(currLink.attr("href"));
            if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
                $('.menuitem').removeClass("active");
                currLink.addClass("active");
            } else {
                currLink.removeClass("active");
            }
        });    
    })    
});

Since there was no included css, I made some adjustments but it appears to be working fine.

css

#a,#b,#c,#d,#site-main { height: 400px;}
#main-navigation {
    position: fixed;
    top: 0px;
    right:10px;        
}

#main-navigation ul li {
  display: inline;
}

.active {
  background: #f00;
}

Update 1 I updated the code to match the fiddle, now it changes the menu item selection when you scroll to an item.

Answer №2

To properly iterate through the list items, it is important to use a loop and ensure the correct id selector format by adding a # symbol:

$('#main-navigation li').each(function () { //<---ensure correct id selector
    var currentLink = $(this);
    var referencedElement = $(currentLink.find('a').attr("href")); //<---locate the anchor element here
    if (referencedElement.position().top <= scrollPosition && referencedElement.position().top + referencedElement.height() > scrollPosition) {
        $('#main-navigation ul li').removeClass("active"); //<---adjust active class removal here
        currentLink.addClass("active");
    }
    else{
        currentLink.removeClass("active");
    }
});

Answer №3

If you want to implement this feature, I recommend checking out this awesome little plugin: https://github.com/davist11/jQuery-One-Page-Nav

With this plugin, you can automatically add a "current" class when navigating through different sections or divs. I hope this solution works for you! :)

Answer №4

replace

$('a').each(function () {
        $(this).removeClass('active');
    })
    $(this).addClass('active');

with

$('a').each(function () {
    $(this).parent().removeClass('active');
})
$(this).parent().addClass('active');

complete code:

$('a[href^="#"]').on('click', function (e) {
e.preventDefault();
$(document).off("scroll");

$('a').each(function () {
    $(this).parent().removeClass('active');
})
$(this).parent().addClass('active');

var target = this.hash,
    menu = target;
$target = $(target);
$('html, body').stop().animate({
    'scrollTop': $target.offset().top+2
}, 800, 'swing', function () {
    window.location.hash = target;
});

});

No need of onScroll function

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Including HTML attributes within a PHP script

Currently, I am working on developing a message to be displayed to the user after they have submitted the contact form using PHP. However, I am facing an issue with styling the message as intended, particularly with elements like bold text and line breaks ...

What could be causing the malfunction of DirectionalLight in three.js?

While working on a project in three.js to create a ring, I encountered an issue with lighting. The camera setup is fine, but I'm facing difficulties with lighting. Below is my code snippet: <script src="three.js"></script> <sc ...

Expanding Image with HTML and CSS: A Guide

In the process of creating my website, I encountered an issue with the logo placement. I wanted the logo to be in the top left corner, similar to the one shown in this image. Initially, everything was working fine until I made some adjustments to move the ...

Divide text to reduce its width within the confines of a specific height on a div element

I've spent the past week scouring various forums, including stackoverflow, but I haven't been able to find a solution. In my responsive website, I'm using CSS flexbox to display dynamic menu items. Sometimes the text can be quite long, and ...

"Creating a Miniview Panel on a Fabric Canvas: A Step-by-Step Guide

Is there a way to add a mini view panel to my Fabric Canvas like shown in this image MiniView Panel? I want a panel in the corner of my canvas that displays the entire layout. Similar to the panel on this website . ...

What is the method to determine the index of a removed element within a subarray?

In my array, all = [2,3,4,12, 55,33], there is a sub-array named ar1 = [12, 55, 33] starting from the value 12. If I remove a value from all that is present in ar1 (e.g. 12), how can I determine the index of this value in ar1 (0 for 12) so I can also remo ...

Showcasing two sets of data from an array using chart.js within a node.js environment

I am currently working on a project where I need to display two elements from an array - one as the label (e.g. "name of certain type of crop") and the other as the data itself (e.g. "quantity of the crop"). However, I am facing an issue where if the same ...

Substitute "Basic Authentication" with "Form Authentication"

Is there a way in W20 to switch from using "Basic Authentication" to "Form Authentication"? The current documentation mentions only the use of "basicAuth" and does not provide information on implementing form authentication. Our app is built with Angular ...

VueJS added a new object to the array, but the data did not update reactively

Here is my current data layout days: [ { id: 0 name: 'Monday', times: [] }, { id: 1 name: 'Tuesday', times: [] } } I have implemented the following function to append an object to the times array. onT ...

Looking to resolve an issue that is arising in the antd tree component

Having trouble with the antd tree component - when searching and checking/unchecking a field, all previously selected data is unchecked. I want to only update the field that is currently being changed. Not sure how to fix this issue, any help would be appr ...

Retrieve data from two databases and display the information from two separate arrays on a single ejs page after making two separate database calls

Currently, I am faced with a challenge where I need to render a page by passing two arrays that are populated by two separate database calls. It seems that when I only pass one array to the ejs page, everything works as expected. For a single array, my a ...

Refresh the information displayed in the HTML table

I've been assigned the task of developing a shift management web app to help our current employees track their shifts. The website StaffHub has been suggested as a reference for my web app, and I am currently focused on implementing the calendar featu ...

Use HTML to showcase an image that dynamically changes based on the outcome of a query function

Hello there, I hope my inquiry is clear enough. I apologize for reaching out as I am unsure where to begin and what exactly I should be focusing on. Currently, I have an image displayed in an HTML page like this: <div id="tag_sunrise_sunset"><p ...

calls to res.send and res.render

I'm currently trying to determine if it's possible to use res.send(data) and res.render('reports') simultaneously in my code. To provide more details, when I navigate to '/reports', on the server side I am executing a REST ca ...

When using the background-blend-mode property, transition-duration is not taken into account

Try hovering over the top left h1 element in this code example You'll notice that it smoothly changes the h1 element to a shade of blue in just 2 seconds. However, the transition doesn't affect the blend mode of the backgrounds. Even though the ...

What is the best way to retrieve the value of an input field in React when incorporating Material UI components?

I am working with a few radio input components that have been imported from material Ui react. Each radio input is wrapped in a FormControlLabel component. <FormControlLabel onClick={checkAnswerHandler} value={answer} control={<Radio color=&quo ...

Troubleshooting Issue with Query Functionality in MEAN App's Find Request

I'm facing some challenges while working with queries in my MEAN App. Specifically, I am attempting to retrieve data that matches the input entered into a search field: $scope.searchInput = function(search){ $http({ method: 'GET', url: ...

Unable to hear sound - JavaScript glitch

Spent countless hours trying to figure this out - Can't get the audio file to play when clicking an HTML element. var sound = document.querySelector(".soundfile"); function playAudio() { sound.play(); } document.querySelector(".btn-hold").addE ...

Incorporating numerous dropdown menus to a table filled with data retrieved from a database

I have implemented a dynamic table that adds a new row at the end when a button is clicked. One of the columns in the table contains a dropdown list, and I want this dropdown list to display values from a database. This is how I am constructing my table ...

Measuring Internet speed using Node.js

Is there a way to measure internet speed in server-side node.js? I am currently sending the current timestamp from the client side while making an http request. Client side code: var image = document.createElement("img"); image.width = 1; i ...