Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trouble with an one page navigation - updating/highlighting active state and scroll to anchor

I'm already busy with a one page navigation. Below you will find more information about the problem and my wish.

How should it work?

Once a navigation item is clicked, scroll to its particular section and update the active class of the menu. And if the page is scrolled to its particular section, it should update the active state too - so change the class to its particular anchor.

Fixed header

For the website I also used a fixed header, so this should NOT be overlay the particular section. So the menu should stop scrolling when the bottom of the header is reaching the top of the section.

Variable sections

All sections on the one page design has a different height.

Problem

I have already tried a lot of code, to get it work. Most of the code is working, but this isn’t in all browsers the same. Also I have some trouble with updating the active state of the particular section - that match to the active anchor.

Code

I used jQuery to create a smooth scroll to anchor. You can see my working code at JSfiddle.

Here are all resources:

JS

Here I controle the click function of the navigation. So when the user click a list item of #primary-navwrapper, then change the active state class and scroll to the particular section, that match with the clicked anchor.

$('#primary-navwrapper li').find('a[href^="#"]').click(function(event) { 
    // Prevent from default action to intitiate
    event.preventDefault();


    $('#primary-navwrapper li a').removeClass("current");
    $(this).addClass("current");


    // The id of the section we want to go to.
    var anchorId = $(this).attr("href");
    
    // Our scroll target : the top position of the
    // section that has the id referenced by our href.
    var target = $(anchorId).offset().top - offset;
    //console.log(target);
    
    
    $('html, body').animate({ scrollTop: target }, 500, function () {
        //window.location.hash = '!' + id;
        window.location.hash = anchorId;        
    });
    

});

Beside the click function, I also want that when the user scrolls around the one page, it will automatically update the active statement.

function setActiveListElements(event){
    // Get the offset of the window from the top of page
    var windowPos = $(window).scrollTop();
    
    $('#primary-navwrapper li a[href^="#"]').each(function() { 
        var anchorId = $(this);
        var target = $(anchorId.attr("href"));
        
        if (target.length > 0) {
            if (target.position().top <= windowPos && target.position().top + target.height() > windowPos) {
                $('#primary-navwrapper li a').removeClass("current");
                anchorId.addClass("current");
            }
        }
    });
}

$(window).scroll(function() {
    setActiveListElements();
});

In above code, I think that the line of if (target.position().top <= windowPos && target.position().top + target.height() > windowPos) isn’t correct and maybe to long..

If there are any questions or something, I like to hear from you. Casper

like image 856
Caspert Avatar asked Feb 23 '15 16:02

Caspert


1 Answers

Looking at your code, I've updated the line you said for the below one:

if (target.position().top - $('#header').outerHeight() <= windowPos) {
    $('#primary-navwrapper li a').removeClass("current");
    anchorId.addClass("current");
}

In this way, it'll get the target's difference to the top minus the header's height (as it will be always visible) and then check with the window's position. If it's inferior, the user has passed this anchor, so the correspondent link in the menu gets highlighted.

And your first link doesn't have its anchor in the fiddle, so:

<li><a href="#hero" class="current">Home</a></li>

After these changes, everything seems to work fine.

JSFiddle: http://jsfiddle.net/8n06pvy9/17/

EDIT:
To update the hash, I've tried to use a simple window.location.hash = anchorId;, but it results in some weird scroll issues in FF and IE. I've spent some time on it, but I wasn't able to figure out what happens.

So, I suggest a trick that I've already used, using #! in the hash. In this way, your code would be like that:

window.location.hash = '#!' + anchorId.replace('#', '');

And in the scroll function, like that:

window.location.hash = '#!' + anchorId.attr('href').replace('#', '');

JSFiddle: http://jsfiddle.net/8n06pvy9/18/

And, if you want, you can check for the hash in pageload, remove the exclamation point and scroll the page to the desired anchor. Or, if you want to avoid all of that, you can always use some history plugins, like this one. In your case, I personally wouldn't use a plugin for that, but it's your call if it worth it or not.

Hope it helps!

like image 177
Everton Lenger Avatar answered Oct 03 '22 11:10

Everton Lenger