It's done by binding to the scroll event of the container (usually window).
Quick example:
// Cache selectors
var topMenu = $("#top-menu"),
topMenuHeight = topMenu.outerHeight()+15,
// All list items
menuItems = topMenu.find("a"),
// Anchors corresponding to menu items
scrollItems = menuItems.map(function(){
var item = $($(this).attr("href"));
if (item.length) { return item; }
});
// Bind to scroll
$(window).scroll(function(){
// Get container scroll position
var fromTop = $(this).scrollTop()+topMenuHeight;
// Get id of current scroll item
var cur = scrollItems.map(function(){
if ($(this).offset().top < fromTop)
return this;
});
// Get the id of the current element
cur = cur[cur.length-1];
var id = cur && cur.length ? cur[0].id : "";
// Set/remove active class
menuItems
.parent().removeClass("active")
.end().filter("[href='#"+id+"']").parent().addClass("active");
});?
See the above in action at jsFiddle including scroll animation.
If you want the accepted answer to work in JQuery 3 change the code like this:
var scrollItems = menuItems.map(function () {
var id = $(this).attr("href");
try {
var item = $(id);
if (item.length) {
return item;
}
} catch {}
});
I also added a try-catch to prevent javascript from crashing if there is no element by that id. Feel free to improve it even more ;)
Just to complement @Marcus Ekwall 's answer. Doing like this will get only anchor links. And you aren't going to have problems if you have a mix of anchor links and regular ones.
jQuery(document).ready(function(jQuery) {
var topMenu = jQuery("#top-menu"),
offset = 40,
topMenuHeight = topMenu.outerHeight()+offset,
// All list items
menuItems = topMenu.find('a[href*="#"]'),
// Anchors corresponding to menu items
scrollItems = menuItems.map(function(){
var href = jQuery(this).attr("href"),
id = href.substring(href.indexOf('#')),
item = jQuery(id);
//console.log(item)
if (item.length) { return item; }
});
// so we can get a fancy scroll animation
menuItems.click(function(e){
var href = jQuery(this).attr("href"),
id = href.substring(href.indexOf('#'));
offsetTop = href === "#" ? 0 : jQuery(id).offset().top-topMenuHeight+1;
jQuery('html, body').stop().animate({
scrollTop: offsetTop
}, 300);
e.preventDefault();
});
// Bind to scroll
jQuery(window).scroll(function(){
// Get container scroll position
var fromTop = jQuery(this).scrollTop()+topMenuHeight;
// Get id of current scroll item
var cur = scrollItems.map(function(){
if (jQuery(this).offset().top < fromTop)
return this;
});
// Get the id of the current element
cur = cur[cur.length-1];
var id = cur && cur.length ? cur[0].id : "";
menuItems.parent().removeClass("active");
if(id){
menuItems.parent().end().filter("[href*='#"+id+"']").parent().addClass("active");
}
})
})
Basically i replaced
menuItems = topMenu.find("a"),
by
menuItems = topMenu.find('a[href*="#"]'),
To match all links with anchor somewhere, and changed all that what was necessary to make it work with this
See it in action on jsfiddle
Just check my Code and Sniper and demo link :
// Basice Code keep it
$(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
}, 500, 'swing', function () {
window.location.hash = target;
$(document).on("scroll", onScroll);
});
});
});
// Use Your Class or ID For Selection
function onScroll(event){
var scrollPos = $(document).scrollTop();
$('#menu-center a').each(function () {
var currLink = $(this);
var refElement = $(currLink.attr("href"));
if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
$('#menu-center ul li a').removeClass("active");
currLink.addClass("active");
}
else{
currLink.removeClass("active");
}
});
}
$(document).ready(function () {_x000D_
$(document).on("scroll", onScroll);_x000D_
_x000D_
//smoothscroll_x000D_
$('a[href^="#"]').on('click', function (e) {_x000D_
e.preventDefault();_x000D_
$(document).off("scroll");_x000D_
_x000D_
$('a').each(function () {_x000D_
$(this).removeClass('active');_x000D_
})_x000D_
$(this).addClass('active');_x000D_
_x000D_
var target = this.hash,_x000D_
menu = target;_x000D_
$target = $(target);_x000D_
$('html, body').stop().animate({_x000D_
'scrollTop': $target.offset().top+2_x000D_
}, 500, 'swing', function () {_x000D_
window.location.hash = target;_x000D_
$(document).on("scroll", onScroll);_x000D_
});_x000D_
});_x000D_
});_x000D_
_x000D_
function onScroll(event){_x000D_
var scrollPos = $(document).scrollTop();_x000D_
$('#menu-center a').each(function () {_x000D_
var currLink = $(this);_x000D_
var refElement = $(currLink.attr("href"));_x000D_
if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {_x000D_
$('#menu-center ul li a').removeClass("active");_x000D_
currLink.addClass("active");_x000D_
}_x000D_
else{_x000D_
currLink.removeClass("active");_x000D_
}_x000D_
});_x000D_
}
_x000D_
body, html {_x000D_
margin: 0;_x000D_
padding: 0;_x000D_
height: 100%;_x000D_
width: 100%;_x000D_
}_x000D_
.menu {_x000D_
width: 100%;_x000D_
height: 75px;_x000D_
background-color: rgba(0, 0, 0, 1);_x000D_
position: fixed;_x000D_
background-color:rgba(4, 180, 49, 0.6);_x000D_
-webkit-transition: all 0.4s ease;_x000D_
-moz-transition: all 0.4s ease;_x000D_
-o-transition: all 0.4s ease;_x000D_
transition: all 0.4s ease;_x000D_
}_x000D_
.light-menu {_x000D_
width: 100%;_x000D_
height: 75px;_x000D_
background-color: rgba(255, 255, 255, 1);_x000D_
position: fixed;_x000D_
background-color:rgba(4, 180, 49, 0.6);_x000D_
-webkit-transition: all 0.4s ease;_x000D_
-moz-transition: all 0.4s ease;_x000D_
-o-transition: all 0.4s ease;_x000D_
transition: all 0.4s ease;_x000D_
}_x000D_
#menu-center {_x000D_
width: 980px;_x000D_
height: 75px;_x000D_
margin: 0 auto;_x000D_
}_x000D_
#menu-center ul {_x000D_
margin: 0 0 0 0;_x000D_
}_x000D_
#menu-center ul li a{_x000D_
padding: 32px 40px;_x000D_
}_x000D_
#menu-center ul li {_x000D_
list-style: none;_x000D_
margin: 0 0 0 -4px;_x000D_
display: inline;_x000D_
_x000D_
}_x000D_
.active, #menu-center ul li a:hover {_x000D_
font-family:'Droid Sans', serif;_x000D_
font-size: 14px;_x000D_
color: #fff;_x000D_
text-decoration: none;_x000D_
line-height: 50px;_x000D_
background-color: rgba(0, 0, 0, 0.12);_x000D_
padding: 32px 40px;_x000D_
_x000D_
}_x000D_
a {_x000D_
font-family:'Droid Sans', serif;_x000D_
font-size: 14px;_x000D_
color: black;_x000D_
text-decoration: none;_x000D_
line-height: 72px;_x000D_
}_x000D_
#home {_x000D_
background-color: #286090;_x000D_
height: 100vh;_x000D_
width: 100%;_x000D_
overflow: hidden;_x000D_
}_x000D_
#portfolio {_x000D_
background: gray; _x000D_
height: 100vh;_x000D_
width: 100%;_x000D_
}_x000D_
#about {_x000D_
background-color: blue;_x000D_
height: 100vh;_x000D_
width: 100%;_x000D_
}_x000D_
#contact {_x000D_
background-color: rgb(154, 45, 45);_x000D_
height: 100vh;_x000D_
width: 100%;_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<!-- <div class="container"> --->_x000D_
<div class="m1 menu">_x000D_
<div id="menu-center">_x000D_
<ul>_x000D_
<li><a class="active" href="#home">Home</a>_x000D_
_x000D_
</li>_x000D_
<li><a href="#portfolio">Portfolio</a>_x000D_
_x000D_
</li>_x000D_
<li><a href="#about">About</a>_x000D_
_x000D_
</li>_x000D_
<li><a href="#contact">Contact</a>_x000D_
_x000D_
</li>_x000D_
</ul>_x000D_
</div>_x000D_
</div>_x000D_
<div id="home"></div>_x000D_
<div id="portfolio"></div>_x000D_
<div id="about"></div>_x000D_
<div id="contact"></div>
_x000D_
Source: Stackoverflow.com