Sticky scroll with active class on navigation items - javascript

I currently have a sticky scroll navigation that changes the active class as it passes each section on the page. It is a little buggy though...
heres my jquery code:
var s = $("#page-nav"),
pos = s.position();
$(window).scroll(function() {
var windowpos = $(window).scrollTop();
if (windowpos >= pos.top) {
s.addClass("stick");
$('.main').css('margin-top', '60px');
} else {
s.removeClass("stick");
$('.main').removeAttr('style');
}
});
$(window).scroll(function() {
var y = $(this).scrollTop();
$('.linky').each(function (event) {
id = $(this).attr('href');
if (y >= $(id).offset().top) {
$('.linky').not(this).removeClass('active');
$(this).addClass('active');
}
});
});
//page nav
$("#page-nav li a").click(function(e) {
e.preventDefault();
$("#page-nav li a").removeClass('active');
$(this).addClass('active');
goToByScroll($(this).attr("href").replace("#", ""));
});
function goToByScroll(id){
$('html,body').animate({
scrollTop: $("#"+id).offset().top},
'slow');
}
And here is a working example over at codepen
How can I improve my javascript so that is performs a little smoother. As you can see when you click a link it still thinks it is within that section and the active class flickers.

Your problem was that you were setting the active class when you click an link and also when you move. What I did was to remove the class handling from the click event handler and let the scroll handler take care of everything. This way, there is no flicker. Here you have an updated CodePen.
$("#page-nav li a").click(function(e) {
e.preventDefault();
goToByScroll($(this).attr("href").replace("#", ""));
});
If this solution is not good enough for you, tell me and I'll think something more sophisticated.
New working solution here.
Basically, I created a variable that indicates whether you have clicked a link or not. If you have, then the scroll handler won't change CSS classes (avoiding the flicker). Then, in the complete handler of your animate function, I set it to false again (allowing for class changes as you scroll):
var s = $("#page-nav"),
pos = s.position(),
linkClicked = false;
$(window).scroll(function() {
var windowpos = $(window).scrollTop();
if (windowpos >= pos.top) {
s.addClass("stick");
$('.main').css('margin-top', '60px');
} else {
s.removeClass("stick");
$('.main').removeAttr('style');
}
});
$(window).scroll(function() {
var y = $(this).scrollTop();
$('.linky').each(function (event) {
id = $(this).attr('href');
if (y >= $(id).offset().top) {
if (!linkClicked) {
$('.linky').not(this).removeClass('active');
$(this).addClass('active');
}
}
});
});
//page nav
$("#page-nav li a").click(function(e) {
e.preventDefault();
$("#page-nav li a").removeClass('active');
$(this).addClass('active');
linkClicked = true;
goToByScroll($(this).attr("href").replace("#", ""));
});
function goToByScroll(id){
$('html,body').animate({
scrollTop: $("#"+id).offset().top},
'slow', function() {
linkClicked = false;
});
}

Related

Close the pop-up menu when user reach the bottom of the page

I have a simple menu that is opening on click.
What I would need is to:
open menu/hamburger on click top left
user read all text and
scrolling down
3.3rd step - close/hide the pop up menu when user reach the end of the page
I kindly ask for a snippet of code to add at my js code
$(document).ready(function() {
$(".toggle-nav").click(function(e) {
e.stopPropagation();
e.preventDefault();
$(this).toggleClass("active");
$(".menu ul").toggleClass("active");
});
$(document).click(function(e){
if(!e.target.closest("ul") && $(".menu a").hasClass("active")){
$(".menu ul").toggleClass("active");
$(".toggle-nav").toggleClass("active");
}
})
});
Here is my codepen to test the function
great solution?
p.s. obviously the action need to work every time the user toggle the hamburger button
Replace your jQuery with this
$(document).ready(function () {
$(".toggle-nav").click(function (e) {
e.stopPropagation();
e.preventDefault();
$(this).toggleClass("active");
$(".menu ul").toggleClass("active");
});
$(document).click(function (e) {
if (!e.target.closest("ul") && $(".menu a").hasClass("active")) {
$(".menu ul").toggleClass("active");
$(".toggle-nav").toggleClass("active");
}
});
$(window).scroll(function () {
var scrollPos = $(document).scrollTop();
var hgt = $('.menu').height();
var win = $(window).height();
if (hgt - win === scrollPos) {
$(".menu ul").toggleClass("active");
$(".toggle-nav").toggleClass("active");
}
});
});
Version 2: With FadeOut
For this option to work you need to add id="fadeout" to the ul element.
$(document).ready(function () {
$(".toggle-nav").click(function (e) {
e.stopPropagation();
e.preventDefault();
$(this).toggleClass("active");
$(".menu ul").toggleClass("active");
});
$(document).click(function (e) {
if (!e.target.closest("ul") && $(".menu a").hasClass("active")) {
$(".menu ul").toggleClass("active");
$(".toggle-nav").toggleClass("active");
}
});
$(window).scroll(function () {
var scrollPos = $(document).scrollTop();
var hgt = $('.menu').height();
var win = $(window).height();
if (hgt - win === scrollPos) {
$('#fadeout').fadeOut("slow", function () {
$(".menu ul").toggleClass("active");
$(".toggle-nav").toggleClass("active");
$('#fadeout').removeAttr("style");
});
}
});
});
I found this code that may help you (it works almost for all the browsers),
It left just the code to close the popup
Update
(the URL in the comment I couldn't add it here).
You can do this using the jQuery scroll to detect if the scroll reach the bottom and add the toggle when true like this:
$(window).scroll(function () {
if ($(window).scrollTop() + $(window).height() == $(document).height()) {
if ($(".menu a").hasClass("active")) {
$(".menu ul").toggleClass("active");
$(".toggle-nav").toggleClass("active");
}
}
});
Codepen
This should work:
var menu = document.getElementById("Menu");
window.onscroll = function(ev) {
if ((window.innerHeight + window.scrollY) >= menu.offsetHeight) {
if($(".menu a").hasClass("active")){
$(".menu ul").toggleClass("active");
$(".toggle-nav").toggleClass("active");
}
}
};
Then on your <ul> tag, give it the id Menu

ol is hiding without entering the if condition

I have this ol with lis into it. On the click of the ol I want the ol to be hidden, but if clicked on the li it should not hide. And this should happen when the screen size is below 768px. Below is my code
jQuery('ol.main-nav').click(function () {
if (jQuery(window).width() <= 768) {
jQuery('ol.main-nav > li ').click(function (event) {
event.stopPropagation();
})
jQuery('ol.main-nav').hide(100);
}
})
Actually, my code is hiding the ol.nav-main. While using the debugger, it doesn't go into the if condition, but still somehow executes the piece of code.
Try this way:
jQuery('ol.main-nav').click(function () {
if (jQuery(window).width() <= 768) {
jQuery('ol.main-nav').hide(100);
}
});
jQuery('ol.main-nav > li ').click(function (event) {
if (jQuery(window).width() <= 768) {
event.stopPropagation();
}
})
You almost never want to hook up an event handler inside another event handler.
You have two choices for what you're describing:
Check when the event occurs to see if it travelled through an li:
jQuery('ol.main-nav').click(function(event) {
var t;
if (jQuery(window).width() <= 768) {
for (t = event.target; t != this; t = t.parentNode) {
if (t.tagName == "LI") {
return; // The click came through an LI
}
}
jQuery('ol.main-nav').hide(100);
}
});
or
Add a handler to the lis that stops propagation. It's much simpler:
jQuery('ol.main-nav').click(function () {
if (jQuery(window).width() <= 768) {
jQuery('ol.main-nav').hide(100);
}
});
jQuery('ol.main-nav > li').click(function(event) {
event.stopPropagation(); // If you like check `width` here too
});
Do a single click event, test if the clicked element is a list item or if the clicked element is a child of a list item:
jQuery('ol.main-nav').click(function (e) {
if (jQuery(window).width() <= 768 && jQuery(e.target).closest('ol.main-nav > li').length) {
e.preventDefault();
} else {
jQuery('ol.main-nav').hide(100);
}
});
demo:https://jsfiddle.net/Lm73y2bL/

Animate some divs only once

I am trying to animate some divs after the user scrolls to a specific position on the page. the problem is that i want it to happen only once. I used Boolean flags but it doesn't seem to like it.
What are u all suggest me to do?
::the code Its not even running
FYI I don't want to use PHP
var once = false;
$(document).ready(function() {
if ($(window).scrollTop() > 760 && once == false) {
$('.hash').each(function(i) {
$(this).fadeOut(0).delay(1000 * i).fadeIn(1000);
});
once = true;
}
)};
Thanks!
From your question
after the user scrolls to a specific position on the page
Listen to scroll event
$(document).ready(function() {
var once = false;
$(document).on('scroll', function(){
if ($(window).scrollTop() > 760 && once==false){
$('.hash').each(function(i) {
$(this).fadeOut(0).delay(1000*i).fadeIn(1000);
});
once=true;
}
});
)};
Alternative from comments. Check if element has a class (or attribute) or not. Below code checks if the element has the data-noanimate attribute. If yes it will not animate, if not it will animate and add data-noanimate so that it will animate once.
$(document).ready(function() {
$(document).on('scroll', function(){
if ($(window).scrollTop() > 760){
$('.hash').each(function(i) {
if($(this).attr('data-noanimate') === undefined){
$(this).attr('data-noanimate','true').fadeOut(0).delay(1000*i).fadeIn(1000);
}
});
}
});
)};
var once=false;
$(document).ready(function() {
if ($(window).scrollTop() > 760 &&once==false)
{
$('.hash').each(function(i) {
$(this).fadeOut(0).delay(1000*i).fadeIn(1000);});
once=true;
}
});
Your brackets on the end of the ready function were flipped.
The other answer is correct, but it can be better like this:
$(function() {
$(window).on('scroll', function(){
if ($(window).scrollTop() > 760) {
$('.hash').each(function(i) {
$(this).fadeOut(0).delay(1000 * i).fadeIn(1000);
});
// without boolean value,you can off `scroll` event
$(window).off('scroll');
}
})
});

Jumpy scroll after scroll-animation with javascript

I have a problem with the scrolling animation. Jumpy scroll occurs when the page is scrolled after scroll-animation. I suspected the scroll-event repeats itself, but I'm not sure. Can you help me with it?
$(document).ready(function(){
var offset;
var anchor = $("#navigation").offset().top;
$(window).bind('mousewheel', function (e) {
offset = $(window).scrollTop();
if (e.originalEvent.wheelDelta < 0) {
//mouse scroll down
console.log('Down: ' + offset + " " + anchor);
if (offset >= anchor) {
// if anchor has been scrolled, user can scroll further
// the problem ocuurs in this block
return true;
} else {
// animate to anchor( nav menu)
$("body, html").animate({
scrollTop: anchor + 1
}, 200);
$("#navigation").addClass("nav-fixed");
return false;
}
} else {
//mouse scroll up
if (offset < anchor) {
$("#navigation").removeClass("nav-fixed");
return true;
}
}});
});
JSFiddle: http://jsfiddle.net/0noms3cs/
Thank you a lot!
Your issue is simple. The scroll event fires over and over again. Your line of thinking behind the cause of this issue is correct, you have a large number of animate events that get stacked up which causes this weird behavior.
You can resolve this issue by adding a boolean variable (such as scrollInitialized) that starts out as false and gets flipped to true once the scroll event has fired once.
Here's the altered JS code. Note: I only added the scrollInitialized variable and a check for it in the if statement.
Edit: I also removed the inner if-else case since it was not necessary using this design.
EDIT 2: I originally misunderstood what you wanted to do. What you need to do was add a scrollLock variable that would only be set to true for the duration of your animation. After thinking about this, I implemented it for you. Here is the Jsfiddle:
https://jsfiddle.net/04gaaapo/1/
Here is the new JS code:
$(document).ready(function () {
var scrollLock = false;
var offset;
var anchor = $("#navigation").offset().top;
$(window).bind('mousewheel', function (e) {
offset = $(window).scrollTop();
// if scroll is NOT locked and we are above the anchor
if (scrollLock === false && offset < anchor) {
if (e.originalEvent.wheelDelta < 0) {
// scrolling down
scrollLock = true;
// animate to anchor( nav menu)
$("body, html").animate({
scrollTop: anchor + 1
}, 200);
// unlock in 250ms
setTimeout(toggleLock, 250);
// add nav class
$("#navigation").addClass("nav-fixed");
} else if (e.originalEvent.wheelDelta > 0) {
// scrolling up
scrollLock = true;
// animate to top of page
$("body, html").animate({
scrollTop: 0
}, 200);
// unlock in 250ms
setTimeout(toggleLock, 250);
// remove nav class
$("#navigation").removeClass("nav-fixed");
}
}
});
function toggleLock() {
scrollLock = !scrollLock;
};
});

Force scroll to top updated

I am trying to force my page to load always on the top. I don't know if I have to define on-refresh or on-load but I've tried everything. I've searched all the previous questions and non of them seemed to work for me. I checked in Chrome and Mozilla. So do you have any suggestions? What should I do?
I tried the following:
1.
$(document).ready(function(){
$(window).scrollTop(0);
});
2.
jQuery('html,body').animate({scrollTop:0},0);
3.
$(window).on('beforeunload', function(){
$(window).scrollTop(0);
});
4.
$(document).ready(function() {
if (window.location.hash) {
//bind to scroll function
$(document).scroll( function() {
var hash = window.location.hash
var hashName = hash.substring(1, hash.length);
var element;
//if element has this id then scroll to it
if ($(hash).length != 0) {
element = $(hash);
}
//catch cases of links that use anchor name
else if ($('a[name="' + hashName + '"]').length != 0)
{
//just use the first one in case there are multiples
element = $('a[name="' + hashName + '"]:first');
}
//if we have a target then go to it
if (element != undefined) {
window.scrollTo(0, element.position().top);
}
//unbind the scroll event
$(document).unbind("scroll");
});
}
});
5.
$('a').on('click', function(e) {
e.preventDefault();
// do your js stuff
$(window).scrollTop(0);
});
6.
$("html, body").animate({
scrollTop: 0
}, 600);
use animate function
$("html, body").animate({
scrollTop: 0
}, 600);

Categories

Resources