Jquery error when binding menu item with scrolling - javascript

I try to bind navigation menu with scrolling. the effect: when I scroll the window browser, the active menu item will highlight.
this is the html:
<ul class="navigation">
<li>HOME</li>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
<li>Link 4</li>
<li>Link 5</li>
<li>BLOG</li>
</ul>
the jquery code is:
$(window).load(function(){
// Cache selectors
var lastId,
topMenu = $(".navigation"),
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 : "";
if (lastId !== id) {
lastId = id;
// Set/remove active class
menuItems
.parent().removeClass("active")
.end().filter("[href=#"+id+"]").parent().addClass("active");
}
});
this error appear on the firbug console:
Error: Syntax error, unrecognized expression: http://localhost/pages/blog/
I figure out that the code does not handle the abslolute url:
var item = $($(this).attr("href"));
then I replaced it with:
var item = $(this).attr('href').split('=');
Now this error has shown:
TypeError: $(...).offset(...) is undefined
if ($(this).offset().top < fromTop)
Note, when I remove the html line :
<li>BLOG</li>
every thing going well. but with it, those error happened, and the menu scrolling affect does not take place.
Do you have any clue, where is the problem? and how can I solve it?

After two days of searching and trying to understand where is the problem, I solve it.( please refer to this page as the resource of my solution
replace this code in jquery:
scrollItems = menuItems.map(function(){
var item = $($(this).attr("href"));
if (item.length) { return item; }
});
with this code:
// Anchors corresponding to menu items
scrollItems = menuItems.map(function(){
var indexItm = $(this).attr('href').indexOf('#');
if (indexItm >= 0) {
var str = $(this).attr('href').substring(indexItm);
var item = $(str);
if (item.length) { return item; }
}
})
;
that's all.
var item = $($(this).attr("href")); in code was creating problem because when you write href="#id" it is var item = $(#id); but when you write href="/blog/" it becomes var item = $(/blog/); which is incorrect.

Related

Sticky navigation not adding active links correctly

I'm trying to implement a navigation. I have used this code to do the same.
<nav>
<ul id="mainNav">
<li class="active">Home</li>
<li>Work</li>
<li>About</li>
<li>Contact</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.
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.

Minimalistic scrollspy changing active class just to a tag

Anyone could solve this? It's a code on minimalistic scrollspy. It adds active class when content is offset.top. I know this code works but could I know if there's a way to change active class to the "a" tag instead of its parent? I removed .parent() but it doesn't seem to work. I don't want the whole li to be active. Thanks in advance.
Codepen: https://jsfiddle.net/mekwall/up4nu/
HTML
<nav class="clearfix">
<ul class="clearfix" id="top-menu">
<li>Services</li>
<li>About Us</li>
<li>Testimonials</li>
<li>Contact Us</li>
</ul>
</nav>
jQuery
// Cache selectors
var lastId,
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 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
}, 300);
e.preventDefault();
});
// 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 : "";
if (lastId !== id) {
lastId = id;
// Set/remove active class -- This is the part.
menuItems
.parent().removeClass("active")
.end().filter("[href='#"+id+"']").parent().addClass("active");
}
});
Here an updated working jsfiddle
First I added a CSS for a.active, because it fits the [li] space so was impossible to discern what was active.
Then you must set starting as active the tag instead the [li]
Now you can change your last 2 javascript rows to
menuItems
.parent().find('a').removeClass("active")
.find('a').end().filter("[href='#"+id+"']").addClass("active");
using find('a') to get the right tag.

Why isn't my jQuery for binding to scroll event working?

I'm trying to create an active menu that inserts the CSS class .active into each list item as the user scrolls and the corresponding id is passed. I tested this in JSFiddle so I know my code is solid, but it's having no affect on the site I'm developing:
http://dev.celebrate-life.com/
Here's the jQuery:
(function($) {
// Selectors
var lastId,
topMenu = $("#menu-mainmenu"),
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 click handler to list items
menuItems.click(function(e){
var href = $(this).attr("href"),
offsetTop = href === "#" ? 0 : $(href).offset().top-topMenuHeight+1;
$('html, body').stop().animate({
scrollTop: offsetTop
}, 300);
e.preventDefault();
});
// 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 : "";
if (lastId !== id) {
lastId = id;
// Set/remove active class
menuItems
.parent().removeClass("active")
.end().filter("[href=#"+id+"]").parent().addClass("active");
}
});
})(jQuery);
And this is my menu structure:
<ul id="menu-mainmenu" class="x-nav">
<li>
List Item
</li>
</ul>
I have CSS for when the "active" class is applied to a list item, but it's not really relevant until I resolve the issue of applying the class to an <li> in the first place.
Why won't this work?

jQuery active class on navigation links not changing

I have searched through similar questions, and feel like I've tried just about everything to no avail.
I want a class ".active" to append to a link in my page's navigation when a user scrolls to the corresponding section of my one-page site. When the user continues to scroll, that active class will disappear from the link and become added to the next link.
The jQuery for the scrolling does work in the code, but nothing else seems to.
Here is my site: http://tendigi.com/staging/
And here are (shortened) the sections of code:
HTML:
<ul id="nav">
<li>ABOUT</li>
<li>TEAM</li>
<li>PORTFOLIO</li>
<li>PROCESS</li>
<li>BRANDS</li>
<li>PRESS</li>
<li>BLOG</li>
<li>MEETUP</li>
<li>CONTACT</li>
</ul>
<section>
<a id="about">Header</a>
Some Text
</section>
<section>
<a id="team">Header</a>
Some Text
</section>
<section>
<a id="portfolio">Header</a>
Some Text
</section>
jQuery:
// Cache selectors
var lastId,
topMenu = $("#nav"),
topMenuHeight = topMenu.outerHeight()+75,
// 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; }
}),
noScrollAction = false;
// 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;
noScrollAction = true;
$('html, body').stop().animate({
scrollTop: offsetTop
},{
duration: 300,
complete: function() {
menuItems
.parent().removeClass("active")
.end().filter("[href=" + href +"]").parent().addClass("active");
setTimeout(function(){ noScrollAction = false; }, 10);
}
});
e.preventDefault();
});
// Bind to scroll
$(window).scroll(function(){
if(!noScrollAction){
// 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");
}
}
});​
This is my first question on stackoverflow, so I apologize if I'm not doing this correctly! And help would be SO APPRECIATED.
Thanks!
From what I can see this is working. The active link is being applied to the <li /> though. If you need it on the <a /> change this:
.end().filter("[href=" + href +"]").parent().addClass("active");
to:
.end().filter("[href=" + href +"]").addClass("active");
Hope that helps!
EDIT
whoops! you need to change the line before as well. So it's:
.parent().removeClass("active")
.end().filter("[href=" + href +"]").parent().addClass("active");
to:
.removeClass("active")
.filter("[href=" + href +"]").addClass("active");
You also look like you need to tweak your threshold by 5 or 6 pixels...

Active on Scroll

I'm trying to highlight a different list item on scroll but I can't get it to work now that I ported it over to this wordpress installation..
Here's my current menu:
<div id="navigation">
<ul>
<li class="nav1">
</li>
<li class="nav2">
</li>
<li class="nav3">
</li>
<li class="nav4">
</li>
<li class="nav5">
</li>
<li class="nav6">
</li>
</ul>
</div>
And Here is the Javascript:
//#### Change Active Menu Item
// Cache selectors
var lastId,
topMenu = $("#navigation ul"),
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 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 }, 300);
e.preventDefault();
});
// 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 : "";
if (lastId !== id) {
lastId = id;
// Set/remove active class
menuItems
.parent().removeClass("active")
.end().filter("[href=#"+id+"]").parent().addClass("active");
}
});
I can tell the script is kind of working since when scrolling it puts the active class on the #post-1 list item, but it does not move from there.
Can anything be seen in the code that is incorrect?
for some reason, adding 'order' => 'ASC' to the menu seemed to get it to work. I don't know why, but its working now

Categories

Resources