8

I have a single page website with the navigation menu position:fixed at the top of the page.

When I click a link from the navigation menu the page scrolls to the appropriate section using this JQuery:

$('a[href^="#"]').live('click',function(event){
     event.preventDefault();
     var target_offset = $(this.hash).offset() ? $(this.hash).offset().top : 0;
     $('html, body').animate({scrollTop:target_offset}, 1200, 'easeOutExpo');
});

What I'd like to happen is when I manually scroll the page $(window).scroll(function(){...});, relevant to the section passing under the navigation menu #navi-container, the navigation link highlights using .addClass('activeNav');

Nasir
  • 4,785
  • 10
  • 35
  • 39
  • FYI, live() is deprecated as of jquery 1.7. You should instead use on, something more like $(document).on('click', 'a[href^="#"]', function() {...}); – danludwig Dec 13 '11 at 21:15
  • I think you're going to need to bind a scroll event to each of the sections, and compare the scrollTop (?) to the top of the window and if it's within a tolerable distance, toggle the navigation item corresponding to it. I'll take a shot at it when I get off of work if I find the time. – JesseBuesking Dec 13 '11 at 21:20
  • `.delegate()` is another alternative to `.live()`: `$(document).delegate('a[href^="#"]', 'click', function () {...});` but `.on()` is the preferred method as of jQuery 1.7. – Jasper Dec 13 '11 at 21:22
  • Possible duplicate of [Change Active Menu Item on Page Scroll?](http://stackoverflow.com/questions/9979827/change-active-menu-item-on-page-scroll) – Adam Jan 19 '16 at 14:21

3 Answers3

6

Check-out this jsfiddle I stumbled across a few days ago, I believe it's just what you're looking for: http://jsfiddle.net/x3V6Y/

$(function(){
    var sections = {},
        _height  = $(window).height(),
        i        = 0;

    // Grab positions of our sections
    $('.section').each(function(){
        sections[this.name] = $(this).offset().top;
    });

    $(document).scroll(function(){
        var $this   = $(this),
            pos     = $this.scrollTop(),
            $parent = {};

        for(i in sections){
            $parent = $('[name="' + i + '"]').parent();
            //you now have a reference to a jQuery object that is the parent of this section

            if(sections[i] > pos && sections[i] < pos + _height){
                $('a').removeClass('active');
                $('#nav_' + i).addClass('active');
            }  
        }
    });
});

I would like to note that if you end-up using this that you re-think the for(i in sections) loop as it is a big hit to performance. If you can, it is an excellent idea to use this kind of loop:

for (var i = 0, len = sections.length; i < len; i++) {
    //...
}

...but that requires a re-think of how to store the offsets of the section elements since this type of loop requires an array rather than an object (an object will work but it has to be zero-indexed and all the indexes have to be integers).

Jasper
  • 75,717
  • 14
  • 151
  • 146
  • 1
    Thanks this is what I'm looking for, but how to I modify the code to check for the parent element of the `.section` class ....the names/id are as follows: `#home, #about, #services, #process, #cost, #contact`...would I use some form of array? – Nasir Dec 13 '11 at 22:40
  • @Nasir Inside the `for(i in sections)` loop you can get the section's parent like this: `$('[name="' + i + '"]').parent()`. This will select the parent of the element by its name (which is what's being stored in the `sections` object). However if there are multiple elements with the same `name` attribute then you will get multiple parents with the selector I just showed you. – Jasper Dec 13 '11 at 22:43
  • I'm having trouble making the amends work...sorry I'm pants at JavaScript. Can you edit your answer and show me, Thanks – Nasir Dec 13 '11 at 22:54
  • @Nasir I updated the answer, check-out the `for` loop and you will see a new variable, `$parent`. – Jasper Dec 13 '11 at 22:57
  • I've tried your amended code, although I'm not quite sure what `sections[this.name] = $(this).offset().top;` is used for??? ...This is the website where I'm trying to implement this http://www.planmore.co.uk/ ...Unfortunately I'm failing miserably. – Nasir Dec 13 '11 at 23:10
  • @Nasir http://jsfiddle.net/x3V6Y/7/ -- If you watch your error console you will see the parent of the `section` elements being logged (for the HTML structure of the demo the parent is the `div#nav` element. – Jasper Dec 13 '11 at 23:43
  • @Nasir When I load your webpage I get the following error: `$active is not defined - http://www.planmore.co.uk/js/planmore.js - Line 19` – Jasper Dec 13 '11 at 23:44
1

This is what I came up with when trying to do this with vanilla js. It works for my purpose and it should be easy to tweak the if statement to fit your needs.

window.addEventListener('load', (e) => {

    let contentWrapper = document.querySelector("main");
    let mainNavLinks = document.querySelectorAll(".glossaryContainer a");

    if( mainNavLinks != null ){

        window.addEventListener("scroll", event => {

            let fromTop = window.scrollY ;
            let selectedEl = '';

            mainNavLinks.forEach(link => {

                let section = document.querySelector(link.hash);

                if ( ( section.offsetTop - section.offsetHeight + contentWrapper.offsetTop ) <= fromTop) {
                    selectedEl = link.parentElement;
                }
                
                link.parentElement.classList.remove("selected");

            });

            if( selectedEl ){
                selectedEl.classList.add("selected");
            }

        });
    }
});
    <nav class="glossaryContainer">
        <ul>
            <li><a href="#a">Section 1</a></li>
            <li><a href="#b">Section 2</a></li>
            <li><a href="#c">Section 3</a></li>
        </ul>
    </nav>

    <main>
        <section id="a">
            <h1>Section 1</h1>
            <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Officiis, blanditiis expedita? Earum eligendi pariatur quaerat quos expedita ab quibusdam ratione veniam in, mollitia fuga repudiandae?</p>
        </section>
        <section id="b">
            <h1>Section 2</h1>
            <p>Ratione nulla nam, ipsa dignissimos corrupti veniam nostrum, laudantium asperiores sequi numquam placeat velit voluptate in praesentium non labore unde incidunt laborum maxime quae magni.</p>
        </section>
        <section id="c">
            <h1>Section 3</h1>
            <p>Soluta quibusdam ad nostrum vel voluptate delectus sequi dolores quia quaerat officia corrupti, aperiam fugit facere debitis repudiandae praesentium sapiente inventore repellendus, nemo commodi alias!</p>
         </section>

    </main>
0

This should solve it. User Manually Scrolls on getting to a section, the Nav Link is Highlighted

let mainNavLinks = document.querySelectorAll(".glossaryContainer ul li a");
let
  mainSections = document.querySelectorAll("main section");
window.addEventListener("scroll", event => {
  let fromTop = window.scrollY;
  mainNavLinks.forEach(link => {
    let section = document.querySelector(link.hash);
    if (section.offsetTop <= fromTop && section.offsetTop + section.offsetHeight >
      fromTop) {
      link.classList.add("current");
    } else {
      link.classList.remove("current");
    }
  });
});
<nav class="glossaryContainer">
  <ul>
    <li><a href="#a">Section 1</a></li>
    <li><a href="#b">Section 2</a></li>
    <li><a href="#c">Section 3</a></li>
  </ul>
</nav>

<main>
  <section id="a">
    <h1>Section 1</h1>
    <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Officiis, blanditiis expedita? Earum eligendi pariatur quaerat quos expedita ab quibusdam ratione veniam in, mollitia fuga repudiandae?</p>
  </section>
  <section id="b">
    <h1>Section 2</h1>
    <p>Ratione nulla nam, ipsa dignissimos corrupti veniam nostrum, laudantium asperiores sequi numquam placeat velit voluptate in praesentium non labore unde incidunt laborum maxime quae magni.</p>
  </section>
  <section id="c">
    <h1>Section 3</h1>
    <p>Soluta quibusdam ad nostrum vel voluptate delectus sequi dolores quia quaerat officia corrupti, aperiam fugit facere debitis repudiandae praesentium sapiente inventore repellendus, nemo commodi alias!</p>
  </section>

</main>

enter image description here