EDIT:
I changed my answer to talk a little about performance and some specific cases.
If you're just looking for code, there is a commented snippet below.
Original answer
Instead of adding the .active class to all links, you should specify the one whose href attribute matches the section identifier.
You can then add the .active class to this link and remove it from the rest.
if (position >= target) { $('#navigation > ul > li > a').removeClass('active'); $('#navigation > ul > li > a[href=#' + id + ']').addClass('active'); }
Using the above modification, your code will correctly highlight the corresponding link. Hope this helps!
Performance improvement
Even when this code does its job, it is far from optimal. In any case, remember:
We must forget about low efficiency, say, in 97% of cases: premature optimization is the root of all evil. Nevertheless, we should not pass up to our capabilities in this critical 3%. (Donald Knut)
Therefore, if you have no performance problems when testing events on a slow device, the best thing you can do is to stop reading and think about the next amazing feature for your project!
There are three main steps to improving performance:
Do as many previous jobs as possible:
To avoid re-searching in the DOM (every time an event is triggered), you can pre-cache your jQuery objects (e.g. on document.ready ):
var $navigationLinks = $('#navigation > ul > li > a'); var $sections = $(".section");
Then you can map each section to the corresponding navigation link:
var sectionIdTonavigationLink = {}; $sections.each( function(){ sectionIdTonavigationLink[ $(this).attr('id') ] = $('#navigation > ul > li > a[href=\\#' + $(this).attr('id') + ']'); });
Note the two backslashes in the binding selector: the hash '#' has special meaning in CSS, so it needs to be escaped (thanks @Johnnie ).
In addition, you can cache the position of each section (Bootstrap Scrollspy does this). But, if you do this, you need to remember to update them every time they change (the user resizes the window, new content is added via ajax, the subsection expands, etc.).
Optimize event handler:
Imagine that a user scrolls inside one section: you do not need to change the active navigation link. But if you look at the code above, you will see that it actually changes several times. Before highlighting the correct link, all previous links will also do this (since the relevant sections also check the position >= target condition).
One solution is to iterate the sections from the bottom up, with the first one whose .offset().top is equal to or less than $(window).scrollTop is correct. And yes, you can rely on jQuery to return objects in DOM order (starting with version 1.3.2 ). To iterate from bottom to top, just select them in reverse order:
var $sections = $( $(".section").get().reverse() ); $sections.each( ... );
Double $() necessary because get() returns DOM elements, not jQuery objects.
Once you have found the correct section, you should return false to exit the loop and not check other sections.
Finally, you shouldn't do anything if the correct navigation link is already highlighted, so check this out:
if ( !$navigationLink.hasClass( 'active' ) ) { $navigationLinks.removeClass('active'); $navigationLink.addClass('active'); }
Trigger the event as little as possible:
The most accurate way to prevent slow or unresponsive events with a high rating (scrolling, resizing ...) is to control the frequency of the event handler call: make sure that you do not need to check which link should be highlighted. 100 times per second! If, in addition to highlighting links, you add some bizarre parallax effect, you can quickly deal with troubles.
Right now, of course, you want to read about throttle, debounce, and requestAnimationFrame. This article is a good lecture and gives you a very good idea of ββthe three of them. In our case, regulation is in line with our needs.
In fact, regulation provides a minimum time interval between two executions of functions.
I implemented the throttle function in snippet. From there, you can get a more sophisticated or, better yet, a library such as underscore.js or lodash (if you don't need the whole library, you can always get the throttle function out of it).
Note: if you look around, you will find simpler gas features. Beware of them, because they can skip the last trigger of the event (and this is the most important!).
Special cases:
I will not include these cases in the fragment, so as not to complicate it.
In the snippet below, links will be highlighted when the section reaches the very top of the page. If you want them to be highlighted earlier, you can add a slight offset as follows:
if (position + offset >= target) {
This is especially useful when you have a top navigation bar.
And if your last section is too small to reach the top of the page, you can select the link corresponding to it when the scroll bar is in the lowest position:
if ( $(window).scrollTop() >= $(document).height() - $(window).height() ) {
Some browser support issues have been considered. You can read more about this here and here .
Fragment and test
Finally, you have a commented snippet. Please note that I changed the name of some variables to make them more visual.
#navigation { position: fixed; } #sections { position: absolute; left: 150px; } .section { height: 200px; margin: 10px; padding: 10px; border: 1px dashed black; } #section5 { height: 1000px; } .active { background: red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="navigation"> <ul> <li><a href="#section1">Section 1</a></li> <li><a href="#section2">Section 2</a></li> <li><a href="#section3">Section 3</a></li> <li><a href="#section4">Section 4</a></li> <li><a href="#section5">Section 5</a></li> </ul> </div> <div id="sections"> <div id="section1" class="section"> I'm section 1 </div> <div id="section2" class="section"> I'm section 2 </div> <div id="section3" class="section"> I'm section 3 </div> <div id="section4" class="section"> I'm section 4 </div> <div id="section5" class="section"> I'm section 5 </div> </div>
And if you're interested, this fiddle checks out the various improvements that we talked about.
Good coding!