Add Class to Menu Item When Scroll - javascript

I want the menu items to look like they are selected when you are scrolling through certain sections of a single page website
I tried capturing the position of each element and store it in a variable and then apply a class when the top of that element passes the top of the page.
Java Script:
var blockone = Math.abs($("#one").position().top);
var blocktwo = Math.abs($("#two").position().top);
var blockthr = Math.abs($("#thr").position().top);
var blockfou = Math.abs($("#fou").position().top);
var blockfiv = Math.abs($("#fiv").position().top);
var blocksix = Math.abs($("#six").position().top);
function removeSelected() {
$('#menu li').removeClass('selected');
}
$("#frame").scroll( function() {
$("#menu li:nth-child(1)").addClass('selected');
var value = $(this).scrollTop();
if ( value < blocktwo ){
removeSelected();
$("#menu li:nth-child(1)").addClass('selected');
} else if (value < blockthr){
removeSelected();
$("#menu li:nth-child(2)").addClass('selected');
} else if (value < blockfou){
removeSelected();
$("#menu li:nth-child(3)").addClass('selected');
} else if (value < blockfiv){
removeSelected();
$("#menu li:nth-child(4)").addClass('selected');
} else if (value < blocksix) {
removeSelected();
$("#menu li:nth-child(5)").addClass('selected');
} else {
removeSelected();
$("#menu li:last-child").addClass('selected');
}
});
What am I missing? I have a working jfiddle here: http://jsfiddle.net/zer0ruth/pgbef/1/

The only problem is that you bind the scroll even on #frame while it should be on window.
But well, I saw that only after playing around with you code, so I have a free optimised code for you: http://jsfiddle.net/pgbef/15/.
Saving you position in array is better than having 6 var and you can had a section without changing code:
var position = [];
$('.block').each(function(){
position.push(Math.abs($(this).position().top))
})
Then your scroll function is way shorter too!
$(window).scroll( function() {
var value = $(this).scrollTop() + $('#menu').height();
$.each(position, function(i){
if(this > value){
$('.selected').removeClass('selected');
$("#menu li").eq(i-1).addClass('selected');
return false;
}
})
});

Related

Caught on a persistent issue with JS, nav bar active class not switching properly

I've been using this code snippet to add in a vertical dot nav to a one page site, which smooth scrolls to a section when one of the links are clicked, and keeps a highlight on the active section. I did a lot of tweaking to the css to make it look how I wanted, and then replaced the sections.
So this is my code in a jsfiddle, and the main issue I am having is the active class not changing properly, and making the side nav bar mess up. I've posted the JS below.
$(document).ready(function(){
$('.awesome-tooltip').tooltip({
placement: 'left'
});
$(window).bind('scroll',function(e){
dotnavigation();
});
function dotnavigation(){
var numSections = $('section').length;
$('#side-nav li a').removeClass('active').parent('li').removeClass('active');
$('section').each(function(i,item){
var ele = $(item), nextTop;
console.log(ele.next().html());
if (typeof ele.next().offset() != "undefined") {
nextTop = ele.next().offset().top;
}
else {
nextTop = $(document).height();
}
if (ele.offset() !== null) {
thisTop = ele.offset().top - ((nextTop - ele.offset().top) / numSections);
}
else {
thisTop = 0;
}
var docTop = $(document).scrollTop();
if(docTop >= thisTop && (docTop < nextTop)){
$('#side-nav li').eq(i).addClass('active');
}
});
}
/* get clicks working */
$('#side-nav li').click(function(){
var id = $(this).find('a').attr("href"),
posi,
ele,
padding = 0;
ele = $(id);
posi = ($(ele).offset()||0).top - padding;
$('html, body').animate({scrollTop:posi}, 'slow');
return false;
});
It works fairly well on the jsfiddle, but I am putting this code into squarespace, and on there it ends up like this where all of the buttons highlight when you try to change active classes.
I have tried to isolate the bug by going through the html, css, and the js, but I don't have enough knowledge of JS to be able to edit the script to fix it.
I would really appreciate any help. Thanks in advance.
EDIT: Link to broken squarespace
In the code snippet, the sample page is designed with continuous sections, so the method ele.next() will return next sections. In your squarespace page, the sections is not continuous, so method ele.next() will return empty and the code is not working.
Your could try to modify function dotnavigation like this
function dotnavigation(){
var numSections = $('section').length;
$('#side-nav li a').removeClass('active').parent('li').removeClass('active');
var sections = $('section');
var i = 0;
for (i = 0; i < sections.length; i++) {
var ele = $(sections[i]), nextTop;
//console.log(ele.next().html());
if (i < sections.length - 1) {
nextTop = $(sections[i + 1]).offset().top;
}
else {
nextTop = $(document).height();
}
if (ele.offset() !== null) {
thisTop = ele.offset().top - ((nextTop - ele.offset().top) / numSections);
}
else {
thisTop = 0;
}
var docTop = $(document).scrollTop();
console.log(thisTop);
if(docTop >= thisTop && (docTop < nextTop)){
$('#side-nav li').eq(i).addClass('active');
}
}
}
I've updated your code sample here.

JS destroy triggered function

I coded JS code to restructure html list items for responsive usage:
$(window).resize(function() {
if ($(window).width() <= 640) {
$(function resizenav() {
var lis = $('.menu-header-menu-container ul li:gt(1)').clone();
$('.menu-header-menu-container ul li:gt(1)').remove();
var newLI = $('<li class="toggle-dropdown">more+</li>')
var newUL = $('<ul class="nested"></ul>');
$('.menu-header-menu-container ul').append(newLI);
$(".toggle-dropdown a").append(newUL);
newUL.append(lis);
});
} else if ($(window).width() > 640) {
//destroy function resizenav()
}
});
Code snippet: https://jsfiddle.net/3o32rj1m/3/
I am seeking a way to destroy resizenav() and restore back the navigation original html structure. Any help is much appreciated?
I came up with a solution too... sorry for the delay. Here's the gist:
var isSmall = false;
$(window).on('load resize', function(){
if(!isSmall && $(window).width() <= 640){
var extraListItems = $('#menu-header-menu > li:gt(1)');
if(extraListItems.size() > 0){
var subList = $('<ul>').addClass('nested-list').append(extraListItems).css('display', 'none');
var toggle = $('<li>').addClass('toggle-nested-list').append("<span class='toggle-text'>more+</span>")
.on('click', function(){
subList.toggle();
if(subList.is(':visible')){ $(this).find('.toggle-text').text('less-'); }
else{ $(this).find('.toggle-text').text('more+'); }
});
toggle.append(subList).appendTo('#menu-header-menu');
}
isSmall = true;
}
else if(isSmall && $(window).width() > 640){
var listItems = $('#menu-header-menu .nested-list li');
listItems.appendTo('#menu-header-menu');
$('#menu-header-menu .toggle-nested-list').remove();
isSmall = false;
}
});
Hopefully I didn't miss anything when copying and pasting my code from jsFiddle, but I've got it all there for sure, with comments. Make sure you just resize the output frame in jsFiddle, as resizing the entire browser window has some strange effects there... https://jsfiddle.net/m4n41wkc/
Try this:
var flag = false;
var original = $('.menu-header-menu-container ul').clone().html();
function resizenav() {
var lis = $('.menu-header-menu-container ul li:gt(1)').clone();
$('.menu-header-menu-container ul li:gt(1)').remove();
var newLI = $('<li class="toggle-dropdown">more+</li>')
var newUL = $('<ul class="nested"></ul>');
$('.menu-header-menu-container ul').append(newLI);
$( ".toggle-dropdown a" ).append(newUL);
newUL.append(lis);
};
$(window).resize(function() {
if ($(window).width() <= 640) {
if(!flag) {
resizenav();
flag = true;
}
} else if ($(window).width() > 640) {
if(flag){
$('.menu-header-menu-container ul').html(original);
flag = false;
}
}
});

Pop up text after scrolling certain distances is not working

I'm trying to get text to pop up at a fixed location at different y-scroll intervals however, I can't seem to chain the scripts correctly. It seems to skip the first action of displaying the first message.
Code: http://jsfiddle.net/k8bnd/
var startY = 1500;
var stopY = 2000;
$(window).scroll(function(){
checkY();
});
function checkY()
{
console.log($(window).scrollTop());
if($(window).scrollTop() > startY && $(window).scrollTop() <= stopY)
{
console.log("Show");
$('.titleTwo').show();
}
else
{
console.log("Hide");
$('.titleTwo').hide();
} }
checkY();
Your functions should have different names. Try
var startY1 = 900;
var stopY1 = 1800;
var startY2 = 1801;
var stopY2 = 2500;
$(window).scroll(function(){
checkY();
});
function checkY()
{
console.log($(window).scrollTop());
if($(window).scrollTop() > startY1 && $(window).scrollTop() <= stopY1)
{
console.log("Show");
$('.foxedDiv').show();
}
else
{
console.log("Hide");
$('.foxedDiv').hide();
}
if($(window).scrollTop() > startY2 && $(window).scrollTop() <= stopY2)
{
console.log("Show");
$('.foxedDivved').show();
}
else
{
console.log("Hide");
$('.foxedDivved').hide();
}
}
checkY();
Fixed Fiddle:
http://jsfiddle.net/k8bnd/1/
This is happening because you have overwritten the previous values/functions. To make this more dynamic you can add data-* attributes to each message element specifying which positions they are valid for. And then in the scroll event check each element's position data, if the window is within that range show it, otherwise hide it.
HTML
<!-- Changed the classes both elements had to foxedDiv
so that we can select them as a group and loop over them -->
<div class="foxedDiv" data-position="900,1800">
MESSAGE 1
</div>
<div class="foxedDiv" data-position="1801,2500">
MESSAGE 2
</div>
JS
//Note you do not need to make an anonymous
//function just to do the call for checkY
//just pass the function
$(window).scroll(checkY);
function checkY(){
//save this value so we dont have to call the function everytime
var top = $(window).scrollTop();
console.log(top);
$(".foxedDiv").each(function(){
var positionData = $(this).data("position").split(",");
if(top > positionData[0] && top <= positionData[1]){
console.log("Show");
$(this).show();
} else {
console.log("Hide");
$(this).hide();
}
});
}
checkY();
JSFiddle Demo

Transition List Item Scrolling - HTML

Here's the FIDDLE.
List item that appears on clicking the up and down arrows.
When I click on it. It appears instantly which I don't want it to be like that.
I wanted to add a transition to it but I don't have any clue whether to apply it on a li or a tag.
Besides I'm here hiding and revealing li items on click. But I need the transition like when I click on the up arrow, I want the hidden element to push the active element downwards and vice versa.
Someone help me in this.
I updated your FIDDLE
you may check it out.
var
cur = 2,
$container = $('.dropdown-container'),
$ul = $('.dropdown'),
$ul_length = $('.dropdown li').children().length,
$up = $('.up'),
$down = $('.down'),
changeLi = function (cur) {
var $cur_li = $('.dropdown li:nth-child(' + cur + ')');
$('.current_selected').removeClass('current_selected');
$cur_li.addClass('current_selected');
$container
.height($cur_li.outerHeight())
.width($cur_li.outerWidth());
$up.offset({
left: ($container.outerWidth() - $up.outerWidth()) / 2
});
$down.offset({
left: ($container.outerWidth() - $down.outerWidth()) / 2
});
$ul.animate({
top: -$cur_li.position().top
}, 200);
};
$up.on('click', function () {
cur--;
if (cur < 1) {
cur = 1;
return false;
}
changeLi(cur);
});
$down.on('click', function () {
cur++;
if (cur > $ul_length) {
cur = $ul_length;
return false;
}
changeLi(cur);
});
changeLi(cur);
You can add animatation like slideUp, slideDown etc.
var cur = 2;
$('.up').click(function() {
if(cur <= 0) return;
var last = cur;
cur --;
$('.dropdown li').eq(last).hide(500).addClass('hide');
$('.dropdown li').eq(cur).show(100).removeClass('hide');
});
$('.down').click(function() {
if(cur >= $('.dropdown li').length - 1) return;
var last = cur;
cur ++;
$('.dropdown li').eq(last).hide(500).addClass('hide');
$('.dropdown li').eq(cur).show(100).removeClass('hide');
});

Prevent scrolling jquery script from running twice

I'm new to jquery and have put together the following code to make a DIV appear after a set scroll-down amount. If scrolling back up, the DIV disappears. Optionally, once the DIV has appeared, there is a link to close it. This all works as intended, apart from that I only want the script to run once. At the moment if I scroll back up, the yellow box appears again. How can I ensure the box stays closed? As another option, could I integrate cookies or localStorage?
Many thanks! Russ.
Javascript:
$(function () {
var target = $(".box");
if ($(window).scrollTop() > 30) {
target.hide();
}
$(window).scroll(function () {
var pos = $(window).scrollTop();
if (pos > 30) {
target.stop(true, true).fadeIn('slow');
} else {
target.stop(true, true).fadeOut('slow');
}
});
$('a.close').click(function () {
$($(this).attr('href')).slideUp();
return false;
});
});
Here is the jsfiddle link to my code: jsfiddle link
You can remove the class to ensure the box stays enclosed with removeClass(). Or directly $(".box").remove() after your animation.
You can store this choice with cookie but if the client deletes his cookies, it's lost.
You can remove event scroll from window and for localStorage do something like that:
$(function () {
var target = $(".box");
if ($(window).scrollTop() > 30) {
target.hide();
}
$(window).scroll(function () {
var pos = $(window).scrollTop();
if (pos > 30) {
target.stop(true, true).fadeIn('slow');
} else {
target.stop(true, true).fadeOut('slow');
}
if(localStorage['noNotification'] == 'true'){
$(window).off('scroll');
}
});
$('a.close').click(function () {
$($(this).attr('href')).slideUp();
$(window).off('scroll');
localStorage['noNotification'] = 'true';
return false;
});
});
try this http://jsfiddle.net/AbwXu/4/
var notdisplayed=true;
$(function(){
var target = $(".box");
if($(window).scrollTop() > 30){
target.hide();
}
$(window).scroll(function(){
var pos = $(window).scrollTop();
if(pos > 30 && notdisplayed){
target.stop(true, true).fadeIn('slow');
} else {
target.stop(true, true).fadeOut('slow');
notdisplayed=false;
}
});
$('a.close').click(function() {
$($(this).attr('href')).slideUp();
notdisplayed=false;
return false;
});

Categories