jQuery class change based on scroll ignoring last section - javascript

I'm using jQuery to guide my pages scrolling and change the color of a nav item to correspond to the section you're in. Everything's working well, but the script is ignoring the last section on the page (Contact).
This is largely based on another question here on Stack, but I've modified the code to fit my needs and then ran into the issue.
Test site:
http://dev4.dhut.ch/
HTML:
<nav>
<ul>
<li class="active">MUSIC</li>
<li>PHOTOS</li>
<li>LYRICS</li>
<li>NEWS</li>
<li>INFO</li>
<li>CONTACT</li>
</ul>
<span></span>
</nav>
JavaScript:
var $sections = $('section'),
$navs = $('nav > ul > li'),
topsArray = $sections.map(function(){
return $(this).position().top - 100;
}).get(),
len = topsArray.length,
currentIndex = 0,
getCurrent = function(top){
for(var i = 0; i < len; i++){
if(top > topsArray[i] && topsArray[i+1] && top < topsArray[i+1]){
return i;
}
}
};
$(document).scroll(function(e){
var scrollTop = $(this).scrollTop(),
checkIndex = getCurrent( scrollTop );
if( checkIndex !== currentIndex ){
currentIndex = checkIndex;
$navs.eq( currentIndex ).addClass('active').siblings('.active').removeClass('active');
}
});

I think the following line is where the problem lies:
if(top > topsArray[i] && topsArray[i+1] && top < topsArray[i+1]){
Specifically topsArray[i+1] is undefined for the last iteration of i. Try pushing one more value into topsArray that includes the height of the entire page.
topsArray = $sections.map(function(){
return $(this).position().top - 100;
}).get(),
topsArray.push(document.height);

Related

How to check if div reaches top of page for each div?

This is my current solution to check if a specific div reaches the top of the page, which i got from here https://stackoverflow.com/a/5279537/4671165
document.addEventListener("scroll", Scroll, false);
function Scroll() {
var top = $('.element').offset().top - $(document).scrollTop();
if (top < 150){
var textvariable = $('.text').text();
}
}
But i want this to do something each time a different div reaches the top of the page, therefore i currently have
var top1 = $('.element1').offset().top - $(document).scrollTop();
var top2 = $('.element2').offset().top - $(document).scrollTop();
var top3 = $('.element3').offset().top - $(document).scrollTop();
if (top1 < 150 && top2 > 150){
var textvariable = $('.text1').text();
}
if (top1 < 150 && top2 < 150 && top3 > 250){
var textvariable = $(.text2').text();
}
if (top2 < 150 && top3 < 250){
var textvariable = $(.text3').text();
}
However, this doesn't seem the most effective way but i can't figure out what is. Especially since i have more elements then just 3 in the project. So i am looking for a more effective way.
I put this together using ES6. I believe this should work. It's been a while since I've used getBoundingClientRect() though.
var divs = document.querySelectAll('div');
document.addEventListener("scroll", Scroll, false);
function Scroll() {
divs.forEach((memo,index) => {
let divTop = memo.getBoundingClientRect().top;
if (divTop <= 0) {
var textvariable = $('.text' + index).text();
});
}
Hope this helps. It should be easier to use and it has a lot of bugs already fixed for you. It's a 1.82 kb file so there's not really much useless stuff into it if added.
I found a jquery solution
function ScrollStart() {
var scrolled = $(this).scrollTop();
/*filter current element at the top with a certain class & give it active class*/
$('.step').removeClass('activetext').filter(function() {
return scrolled <= $(this).offset().top + $(this).height() - 50 && scrolled >= $(this).offset().top - 50;
}).addClass('activetext');
/* make exclusion for first element */
var boven = $('.first').offset().top - $(document).scrollTop();
if (boven > 0){
$('.first').addClass('activetext');
}
/*make exclusion for last element*/
var bottom = $('.last').offset().top - ($('.last').height()/5) - $(document).scrollTop();
if (bottom < 150){
$('.step').removeClass('activetext')
$('.last').addClass('activetext');
}
else{
$('.last').removeClass('activetext')
}
/* give variable 'text' the text of the active class & append it */
var text = $('.activetext .headertekst').text();
$('.dropbtn').empty();
$('.dropbtn').append(text);
$('.dropbtn').append('<img src="images/downarrow.svg" galleryimg="no"></img>');
}

Emberjs slider no working as expected

I build a custom carousel in ember and Jquery. Is pretty much straight forward. when click nextImage the slider move the the next image and when clicked previewsImage the slider goes back to the previews image. That part is working perfect. The problem is that when clicked on goToImage(AKA Dots controllers) the images don't move to the corresponding dots order. Look like my logic may have some problems in the.
<nav class="dots">
<a href="#" class="carousel-bullet "{{action 'goToImage' 1}}></a>
<a href="#" class="carousel-bullet"{{action 'goToImage' 2}}></a>
<a href="#" class="carousel-bullet"{{action 'goToImage' 3}}></a>
</nav>
App.SliderComponent = Ember.Component.extend({
currentIndex: 0,
actions: {
runSlider: function(x){
var currentIndex = this.get('currentIndex');
var indexDiff = this.$().find('.carousel ul li').index();
var carousel = this.$().find('.carousel ul'),
slideWidth = carousel.find('li').width();
console.log(indexDiff);
if(x == 1){
carousel.animate({left: - slideWidth }, 200, function () {
carousel.find('li:first-child').appendTo(carousel);
carousel.css('left', '');
});
}else{
carousel.animate({left: + slideWidth}, 200, function () {
carousel.find('li:last-child').prependTo(carousel);
carousel.css('left', '');
});
}
},
nextImage: function(){
this.send('runSlider', 1);
},
previewsImage: function(){
this.send('runSlider',0);
},
goToImage: function(newIndex){
var currentIndex = this.get('currentIndex')
var indexDiff = newIndex - currentIndex;
var direction = (newIndex > currentIndex) ? 'nextImage' : 'previewsImage';
for (var i = 0; i < indexDiff; i++){
this.send(direction);
}
}
}
});
Your indexDiff may be negative. For example, if new index is 0 and current index is 2. In this case, for loop in your code will not work. I guess changing var indexDiff = newIndex - currentIndex; to var indexDiff = Math.abs(newIndex - currentIndex); will help. If this will not help, I would suggest to put console.log(x); in the first line of runSlider action to look if it is called and what is passed.

Javascript - Mouse Wheel Go to Class

Every time I use the mouse wheel (you know normal scrolling) I want the JS (jquery or whatever) to scroll to a specific class (or id doesn't matter).
I have multiple divs so code like $('body').scrollTo($nextdiv) is not an option.
I just want to make every wheel cycle to move to a next div with a specific class/id. The same for the reverse scroll. To move one div (with a specific class) up.
I found mouse wheel event and how to move to a specific div but can't manage to make it work together.
Animated scroll would be cool.
Simple question. Can I have class AND id in the same div? ex <div class="a" id="b"> ?
Quick example, this code can be improved. Better to test on jsfiddle. Point mouse over list and scroll.
Note: I didn't use class but if you understand what I did it's easy to use classes.
Note 2: I just change color but logic can be replace with anything you want.
demo
demo 2 (with classes)
var i = 0;
var list = document.getElementById("list"), length = list.children.length;
list.addEventListener("wheel", ColorLi);
function ColorLi(e) {
//reset colors
for(var j = 0; j < length; j++)
list.children[j].style.color = "black";
//calculate index
if(e.wheelDelta > 0)
i++;
else
i--;
//fix index out of range
i = i < 0 ? 0 : i;
i = i > length-1 ? length-1 : i;
//set color
list.children[i].style.color = "red";
}
<ul id="list">
<li style="color: red">A</li>
<li>B</li>
<li>C</li>
<li>D</li>
</ul>
You could use the following plugins: jquery.mousewheel and jquery.scrollTo plugin, like:
/*!
* jQuery Mousewheel 3.1.13
*
* Copyright 2015 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
// The actual code:
$(document).ready(function () {
var targets = $('.scroll'); // List of elements to scroll to
var index = 0;
var duration = 500;
var canScroll = true;
var cache;
function limit(x, min, max) {
return Math.min(max, Math.max(min, x));
}
$(window).mousewheel(function (ev) {
if (canScroll) {
cache = index;
if (ev.deltaY < 0) {
index = index + 1; // Scrolling down, so increase index
} else {
index = index - 1; // Scrolling up, so decrease index
}
// Make sure the index is between 0 and (targets.length - 1)
index = limit(index, 0, targets.length - 1);
console.log(index);
// Make sure to scroll if and only if the value has changed
if (index !== cache) {
// Scroll to the target element:
$(window).scrollTo(targets.get(index), {
duration: duration,
easing: 'swing'
});
canScroll = false;
setTimeout(function () {
canScroll = true;
}, duration);
}
}
ev.preventDefault();
return false;
});
});
div {
content: ' ';
height: 500px;
background-color: #f2f2f2;
}
div:nth-child(even) {
background-color: #d0d0d0;
}
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="//cdn.jsdelivr.net/jquery.scrollto/2.1.0/jquery.scrollTo.min.js"></script>
<div class="scroll">div1</div>
<div class="scroll">div2</div>
<div class="scroll">div3</div>
<div class="scroll">div4</div>
<div class="scroll">div5</div>
<div class="scroll">div6</div>

Making a menu class active when scrolled past

Note: Using Bootstrap
I want to assign a menu item the 'active' class when the anchor is 50px away from top of the screen. This means that i need to unassign the active class in js and assign a new one.
This is my Menu
<nav class="navbar navbar-default" data-spy="affix" data-offset-top="34">
<div class="container">
<div class="navbar-collapse collapse navbar-responsive-collapse" id="main-menu">
<ul class="nav navbar-nav">
<li id="whatbutton">What We Are</li>
<li id="whybutton">Why Us</li>
<li id="offerbutton">What We Offer</li>
<li id="contactbutton">Contact Us</li>
</ul>
<ul class="nav navbar-nav pull-right">
<li>Right Link</li>
</ul>
</div>
</div>
</nav>
And these are my h1's
<h1 id="whatissm" name="whatissm"><span>sometexthere</span></h1>
<h1 id="whyusesm" name="whyusesm"><span>somtexthere</span></h1>
<h1 id="whatdoessmoffer" name="whatdoessmoffer"><span>sometexthere</span></h1>
<h1 id="contactus" name="contactus"><span>Contact Us</span></h1>
Now this is where I start to struggle...
from an early post i understand that my js should look something like this
<script>
//smooth scrolling
$(function() {
$('[data-toggle="popover"]').popover();
$('[title]').tooltip({container: 'body'});
});
$('a').click(function() {
var reduce = 150;
$('html, body').animate({
scrollTop: $('[name="' + $.attr(this, 'href').substr(1) + '"]').offset().top - reduce
}, 500);
return false;
});
//Making class active by scrolling past it
$(window).scroll(function(){
var scrollTop = $(document).scrollTop();
var anchors = $('body').find('.anchor');
for (var i = 0; i < anchors.length; i++){
if (scrollTop > $(anchors[i]).offset().top - 50 && scrollTop < $(anchors[i]).offset().top + $(anchors[i]).height() - 50) {
$(anchors[i]).addClass('active');
} else {
$(anchors[i]).removeClass('active');
}
}
});
</script>
But unfortunately that code isn't working for me. 1) It doesn't unassign the active class 2) it's just not working.
Edit: Similar to this http://getbootstrap.com/css/ (when you scroll the active class in the right menu changes)
Edit2: Added my code for the scroll down active effect... i'm fairly new to js so i may have implemented it wrong somehow.
EDIT: Understood the actual issue:
$(window).scroll(function(){
var scrollTop = $(document).scrollTop();
var anchors = $('body').find('h1');
for (var i = 0; i < anchors.length; i++){
if (scrollTop > $(anchors[i]).offset().top - 50 && scrollTop < $(anchors[i]).offset().top + $(anchors[i]).height() - 50) {
$('nav ul li a[href="#' + $(anchors[i]).attr('id') + '"]').addClass('active');
} else {
$('nav ul li a[href="#' + $(anchors[i]).attr('id') + '"]').removeClass('active');
}
}
});
Same technique; checking if each of your h1 elements are both below the elements top position and above the elements bottom position. If true, get the id of the element and select the corrosponding navigation item and add active class to it.
Updated fiddle: http://jsfiddle.net/yrz54fqm/1/
Old answer
Following code should give you the result you're looking for
$(window).scroll(function(){
var scrollTop = $(document).scrollTop();
var anchors = $('body').find('.anchor');
for (var i = 0; i < anchors.length; i++){
if (scrollTop > $(anchors[i]).offset().top - 50 && scrollTop < $(anchors[i]).offset().top + $(anchors[i]).height() - 50) {
$(anchors[i]).addClass('active');
} else {
$(anchors[i]).removeClass('active');
}
}
});
You need to listen for the scroll event on the window, and check if each of your elements are both below the elements top position and above the elements bottom position.
Replace the var anchors = $('body').find('.anchor'); on line 3 with whatever the anchor class names are in your situation. Here's a fiddle with HTML and CSS aswell: http://jsfiddle.net/yrz54fqm/
Hope it helps :)
/**
*
* #param {Element} - element 1 that used to get offset when scrolling
* #param {Element} - activated when passing element 1
* #param {String} - class name to add to element 2 when element 1 is passed
*/
this.ScrollBetween = function(targetElement, pElement, className) {
$(window).scroll(function() {
var scrollTop = $(window).scrollTop(),
targetOffsetTop = $(targetElement).offset().top,
targetHeight = $(targetElement).height();
(scrollTop > targetOffsetTop - 50 && scrollTop < targetOffsetTop + targetHeight - 50 ? $(pElement).addClass(className) : $(pElement).removeClass(className));
});
};
Made a function so it way easier to understand. Right now it is used in a JQuery Object but can be simply changed to regular function by changing it to
function ScrollBetween(targetElement, pElement, className){
.............
}
Can be called by
ScrollBetween($("#section-1"), $("li-1"), "active");

Firefox Not Responding to .addClass

There's a couple things I'm trying to make happen with a sticky nav on a website. I have it working in all browsers, except Firefox.
I want the links to change as the window scrolls.
I want to add a class to change the navigation at a certain point.
Here's the code that's been working in Chrome:
<section id="nav">
<nav>
<ul>
<li id="p1">Snap</li>
<li id="p2">Organize</li>
<li id="p3">Store</li>
<li id="p4">Unify</li>
<li id="p5">Share</li>
<li id="n-play" class="sticky-dl">Get it on Google Play</li>
<li id="n-app" class="sticky-dl">Download on the App Store</li>
</ul>
</nav>
</section>
The Javascript/jQuery
// Auto Nav Active Switching
var nav = $('#nav-sticky-wrapper').height();
var navh = nav+1;
var b = $('section#b').offset().top - navh;
var c = $('section#c').offset().top - navh;
var d = $('section#d').offset().top - navh;
var e = $('section#e').offset().top - navh;
var f = $('section#f').offset().top - navh;
var remove = $('nav ul li a');
setInterval(function() {
if ( $(window).scrollTop() < b ) {
$('section#nav ul').removeClass('stuck');
$('section#nav ul li.sticky-dl').fadeOut(250);
} else {
$('section#nav ul').addClass('stuck');
$('section#nav ul li.sticky-dl').fadeIn(250);
}
if ( $(window).scrollTop() < c ) {
$(remove).removeClass('active');
$('#nb').addClass('active');
} else if ( $(window).scrollTop() >= c && $(window).scrollTop() < d ) {
$(remove).removeClass('active');
$('#nc').addClass('active');
} else if ( $(window).scrollTop() >= d && $(window).scrollTop() < e ) {
$(remove).removeClass('active');
$('#nd').addClass('active');
//bonusoff();
} else if ( $(window).scrollTop() >= e && $(window).scrollTop() < f ) {
$(remove).removeClass('active');
$('#ne').addClass('active');
// Runs Bonus Animation upon Entry
bonus();
} else if ( $(window).scrollTop() >= f ) {
$(remove).removeClass('active');
$('#nf').addClass('active');
}
});
Does Firefox not respond to setInterval or something? Thanks in advance!
setInterval requires a ** delay time** to wait. What is the delay you want? ex: setInterval(function(){...}, 3000);

Categories

Resources