Reputation: 81
I'm trying to implement a navigation. I have used this code to do the same.
<nav>
<ul id="mainNav">
<li class="active"><a href="#home">Home</a></li>
<li><a href="#work">Work</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
<section id="home"><h2>Home</h2></section>
<section id="work" data-sr><h2>Work</h2></section>
<section id="about"><h2>About</h2></section>
<section id="contact"><h2>Contact</h2></section>
// Cache selectors
var lastId,
topMenu = $("#mainNav"),
topMenuHeight = topMenu.outerHeight()+1,
// 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 click handler to menu items
// so we can get a fancy scroll animation
menuItems.click(function(e){
var href = $(this).attr("href"),
offsetTop = href === "#" ? 0 : $(href).offset().top-topMenuHeight+1;
$('html, body').stop().animate({
scrollTop: offsetTop
}, 850);
e.preventDefault();
});
$(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 : "";
if (lastId !== id) {
lastId = id;
// Set/remove active class
menuItems
.parent().removeClass("active")
.end().filter("[href=#"+id+"]").parent().addClass("active");
}
});
The requirement :- I want the navigation to have links which can be swapped as per the requirement.
The issue :- In the above codepen when I swap the links, it does not add the active classes properly. If I swap the navigation links AND the section link also then only it works well. For example, if my navigation has HOME, WORK, ABOUT and CONTACT. Then I should be able to swap the position of the work link with the contact link and still my sticky navigation SHOULD add the active classes correctly WITHOUT shifting their respective sections.
Please help me achieve the above scenario.
Upvotes: 0
Views: 115
Reputation: 350
The below code should solve your problem.
// Cache selectors
var lastId,
topMenu = $("#mainNav"),
topMenuHeight = topMenu.outerHeight()+1,
// All list items
menuItems = topMenu.find("a"),
// Anchors corresponding to menu items
scrollItems = $('section');
// Bind click handler to menu items
// so we can get a fancy scroll animation
menuItems.click(function(e){
var href = $(this).attr("href"),
offsetTop = href === "#" ? 0 : $(href).offset().top-topMenuHeight+1;
$('html, body').stop().animate({
scrollTop: offsetTop
}, 850);
e.preventDefault();
setActiveNav(href);
});
function setActiveNav(href) {
menuItems.each((index, menu) => {
if(menu.hash === href) {
$(menu).parent().addClass('active');
} else {
$(menu).parent().removeClass('active');
}
});
}
$(window).scroll(function(){
// Get container scroll position
var fromTop = $(this).scrollTop();
// Get id of current scroll item
var cur = scrollItems.toArray().findIndex(item => {
return (item.offsetTop >= fromTop);
});
// Get the id of the current element
var id = cur && cur > -1 ? scrollItems[cur].id : "";
if (lastId !== id) {
lastId = id;
// Set/remove active class
menuItems
.parent().removeClass("active")
.end().filter("[href=#"+id+"]").parent().addClass("active");
}
});
Structuring your functionalities in separate functions will solve future maintenance overheads.
Mostly the issues are with structuring, element index and position.
Upvotes: 1