Some very helpful folks wrote and then modified a script to change the class on a nav item when scrolling the page to a related section. Here's the original post: Use jQuery to change a class dependent on scroll position.
My first issue is that the last section of the array is being ignored. Someone else had this question and a solution was offered, which worked for them, but not for me: jQuery class change based on scroll ignoring last section.
The second issue is that even setting aside that the last section in my array is not ever getting selected, the new class name is only getting added to my nav items, but never removed, so that by the time I scroll to the bottom of the page, every item except the last in my nav has "selected" as a class. I'd really appreciate any insight. Here's my code, which is virtually identical to the modified version, except I'm using headings rather than sections (which is maybe significant?):
var $anchs = $('.content h1');
var $navs = $('#zone-submenu div .content > .item-list > ul > li');
topsArray = $anchs.map(function(){
return $(this).position().top - 100;
}).get(),
topsArray.push(window.height);
var len = topsArray.length;
var currentIndex = 0;
var getCurrent = function( top ) { // take the current top position, and see which
for( var i = 0; i < len; i++ ) { // index should be displayed
if( top > topsArray[i] && topsArray[i+1] && top < topsArray[i+1] ) {
return i;
}
}
};
$(document).scroll(function(e) {
var scrollTop = $(this).scrollTop();
var checkIndex = getCurrent( scrollTop );
if( checkIndex !== currentIndex ) {
currentIndex = checkIndex;
$navs.eq( currentIndex ).addClass("selected").siblings(".selected").removeClass(".selected");
}
});
I'm afraid I can't post the site itself - it's in development and I can't reveal it before it goes live. Thanks for any help, though!
This looks a bit odd:
currentIndex = checkIndex;
$navs.eq( currentIndex ).addClass("selected").siblings(".selected").removeClass(".selected");
From what I can tell in your code, it should be:
$navs.eq( currentIndex ).removeClass('selected'); // no dot
currentIndex = checkIndex;
$navs.eq( currentIndex ).addClass("selected");
UPDATED
Fixing the "not displaying the last index item" ...
You are referencing an invalid array element in your getCurrent function - [i+1] is invalid when i == len-1. You need to just return the last element if that happens.
var getCurrent = function( top ) { // take the current top position, and see which
for( var i = 0; i < len-1; i++ ) { // index should be displayed
if( top > topsArray[i] && topsArray[i+1] && top < topsArray[i+1] ) {
return i;
}
}
return len-1;
};
Related
If someone has a better, more descriptive title for this question, let me know; I couldn't think of a good title to describe my problem.
Anyways, I have some jQuery code that allows me to rotate through list items by displaying 3 li elements at a time.
However, I run into a problem when the amount of li elements that I have isn't evenly divisible by 3.
Here is the jQuery:
$( document ).ready(function() {
var j = 1;
var inbetween = 7000; //milliseconds
function page() {
var jmax = $("#leader-slider li").length;
var count = 3;
var start = j;
var end = j + count - 1;
var complete = 0;
console.log(j, start, end);
var range = $('#leader-slider li:nth-child(n+'+ start + '):nth-child(-n+'+ end +')');
range
.fadeIn(400)
.delay(inbetween)
.fadeOut(400, 'swing', function() {
if (j++ >= jmax) {
j = 1;
}
if (++complete >= count) {
page();
}
});
};
page();
});
This works beautifully when I have 3,6,9,12,etc. li elements. But as soon as I have a number not divisible by 3, like 8, it will cycle through all of them but after it shows the last li elements it stops the rotation and no longer shows anymore.
This is happening because your complete is never greater-than or equal-to count if you don't find exactly three elements with your jQuery.
It looks to me like you are already detecting when you've incremented beyond the number of li elements here:
if (j++ >= jmax) {
j = 1;
}
If this is working correctly, you could call your page method here:
if (j++ >= jmax) {
j = 1;
page();
} else if (++complete >= count) {
page();
}
What I want to do is compare each element offset to each other and get the one that is closer to the top of the window, then do something with that specific element
$(".slide").each(function(index, el) {
var $this = $(this);
var offset = $this.offset().top - $(window).scrollTop();
});
so basically, if I print the offset via console.log and I have for each element having the slide class,
I currently get these values :
slide1 = -875
slide2 = 250
slide2 = 850
slide4 = 1375
Slide 2 is the one currently closest to 0 so slide 2 would be the div I would want to do something with...
Hope i am clear enough!
You were already quite close from your goal
var found=null;
var found_top=0;
$(".slide").each(function(index, el) {
var $this = $(this);
var offset = $this.offset().top - $(window).scrollTop();
if( ( found == null ) || ( ( offset >= 0 ) && ( offset < found_top ) ) ){
found=this;
found_top=offset:
}
});
/** do something with found here **/
Bit of a weird problem here - so I will try to explain this as clearly as possible.
I have a simple ng-repeat that will show content based on what has a key value of .active set to true. I let the user scroll through the content with some arrow buttons bound to some ng-clicks. This works great, however I want to exclude one item from the array if it has the key value of side = 'help' attached to it. So basically I want the arrow clicks to skip over it in a sense. I have no control unfortunately where in the array the help item is. So here are the click functions
//flip right
$scope.flipRight = function(index, parent){
var idx = index + 1;
if (idx >= $scope.contentHere[parent].sides.length) {
idx = 0;
}
$scope.contentHere[parent].sides[index].active = false;
$scope.contentHere[parent].sides[idx].active = true;
};
//flip left
$scope.flipLeft = function(index, parent){
var idx = index - 1;
if (idx < 0) {
idx = $scope.contentHere[parent].sides.length - 1;
}
$scope.contentHere[parent].sides[index].active = false;
$scope.contentHere[parent].sides[idx].active = true;
};
So basically what I am trying to figuire out is how to have this logic skip over the item if it has .side = 'help'. I thought about using lodash to _filter the array by items that do not have the value, but it will offset the index so that will not work. I am not sure how to approach this (maybe I am thinking about this incorrectly?), and could use some direction.
Thank you for taking the time to read!
$scope.flipRight = function(index, parent){
var idx = index + 1;
if(idx >= $scope.contentHere[parent].sides.length){
idx = 0;
}
if($scope.contentHere[parent].sides[idx] == 'help'){
$scope.flipRight(idx, parent); //Added to skip over to next item
$scope.contentHere[parent].sides[index].active = false; // Added for the first item does not turn .active to false Issue
return; // Added to skip execution of following line of codes incase of recursion
}
$scope.contentHere[parent].sides[index].active = false;
$scope.contentHere[parent].sides[idx].active = true;
};
//flip left
$scope.flipLeft = function(index, parent){
var idx = index - 1;
if (idx < 0) {
idx = $scope.contentHere[parent].sides.length - 1;
}
if($scope.contentHere[parent].sides[idx] == 'help'){
$scope.flipLeft(idx, parent); //Added to skip over to next item
$scope.contentHere[parent].sides[index].active = false; // Added for the first item does not turn .active to false Issue
return; // Added to skip execution of following line of codes incase of recursion
}
$scope.contentHere[parent].sides[index].active = false;
$scope.contentHere[parent].sides[idx].active = true;
};
In my code i'm trying to compare the height with the position of an element, to ensure that the element doesn't leave the game's div.
First I get the position of my element, snake. and if the cursor is too close then i move it. Then at the end I do a check that it is at least 20 pixels away from the top and bottom. For some reason everything is working except the when it reaches the bottom of the screen (which is the else if statement in the end block of code)
var posL = $("#snake").position().left;
var posT = $("#snake").position().top;
if((e.pageX-200 < posL) && (posL < e.pageX-50)){
if(posL > 20){
posL = posL - 5;
}else{
posT = posT + 5;
}
...
if(posT < 20){
posT = 20;
}else if(posT > parseInt($("#game").height)){
posT = parseInt($("#game").height) - 20;
}
You forgot the () after .height ...twice! ;-)
if(posT < 20) {
posT = 20; // ------------------------v
} else if(posT > parseInt($("#game").height)){
posT = parseInt($("#game").height) - 20;
} // -----------------^
I'm modifying the jquery ui slider. I have certain "stops" I want the user to be able to slide to, expressed as a percentage of the slider's overall width. So, for example, if I have 3 stops, they will be distributed evenly at 0, 50, and 100 (%). I store these in an array [0,50,100].
When the user drags the slider and releases, I capture the slider's current value. So if he scrolled 56% of the way across the bar, his stopVal is 56.
How do I write a function that will then determine which number in the array this stopVal is closest to? Here's my code:
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
//NOW NEED TO FIND CLOSEST ARRAY VALUE TO stopVal
}
This function will let you do that:
Array.prototype.closest = function(value) {
var i;
function diff(n) {
var diff = n - value;
return diff < 0 ? -diff : diff;
}
var found = this[0],
mindiff = diff(found);
for (i = 1 ; i < this.length ; i++) {
var currentdiff = diff(this[i]);
if (currentdiff < mindiff) {
found = this[i];
mindiff = diff(found);
}
}
return found;
}
Now you can do this:
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
//NOW NEED TO FIND CLOSEST ARRAY VALUE TO stopVal
stopVal = optValArr.closest(stopVal);
}
NOTE: Some people consider defining prototypes for native types as dangerous as it can cause conflicts if two libraries do the same (just like global variable). If you are writing a public library you should therefore avoid adding to the prototypes of native types.
Try This:
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
var diff=101;
var val =0;
for(var i =0; i < optValArr.length; i++){
var tmpDiff = Math.abs(stopVal - optValArr[i]);
if(tmpDiff < diff){
diff=tmpDiff;
val = optValArr[i]
}
}
}
slideStop("something", {"value":20});
Demo: http://jsfiddle.net/ggzZj/
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
var closestVal = optValArr.reduce(function (memo, curr) {
var currDiff = Math.abs(curr - stopVal),
memoDiff = Math.abs(memo - stopVal)
return memoDiff < currDiff ? memoDiff : currDif
})
}
Another common definition of "closer" is based on the square of the difference. But, you could do that by simply adding the number that you want in your original array like this:
[10,40,50, my_number]
Then, sort your array and then you choice if you want the closest position from the right or the left.
What do you think?