So this is a bit of a head scratcher and I've resorted to asking for help with it. I have created a series of functions with jQuery. Each function contains an animate() queue and ends with a call back that loads the next function, again with it's own animate() queue. Once it reaches the end it calls the first function again and around we go. I have separated the queues into independent functions, because I want to be able to jump to specific points in the loop based on user clicks. So in the code below the loop runs through once, but when it goes back to the beginning the show() and hide() bits don't appear to be doing anything. Any help with this is greatly appreciated!
var firstItem = jQuery('#vehicle-banner-one');
var firstThumb = jQuery('#thumb-one');
var secondItem = jQuery('#vehicle-banner-two');
var secondThumb = jQuery('#thumb-two');
var thirdItem = jQuery('#vehicle-banner-three');
var thirdThumb = jQuery('#thumb-three');
var nextItem = firstItem;
var nextThumb = firstThumb;
firstItem.hide();
secondItem.hide();
thirdItem.hide();
function leadIn(){
console.log('leadIn');
thirdItem.css({zIndex:8});
secondItem.css({zIndex:9});
firstItem.css({zIndex:10});
firstItem.fadeIn("slow", function(){ holdOne(); });
}
function holdOne(){
console.log('holdOne');
thirdItem.css({zIndex:8}).hide();
secondItem.css({zIndex:9}).hide();
firstItem.css({zIndex:10}).show();
firstItem.delay(3000).delay(0, function(){ transTwo(); });
};
function transTwo(){
console.log('transTwo');
thirdItem.css({zIndex:8}).hide();
secondItem.css({zIndex:10}).hide();
firstItem.css({zIndex:9}).show();
secondItem.fadeIn("slow" , function(){ holdTwo(); });
};
function holdTwo(){
console.log('holdTwo');
thirdItem.css({zIndex:8}).hide();
secondItem.css({zIndex:10}).show();
firstItem.css({zIndex:9}).hide();
secondItem.delay(3000).delay(0, function(){ transThree(); });
};
function transThree(){
console.log('transThree');
thirdItem.css({zIndex:10}).hide();
secondItem.css({zIndex:9}).show();
firstItem.css({zIndex:8}).hide();
thirdItem.fadeIn("slow" , function(){ holdThree(); });
};
function holdThree(){
console.log('holdThree');
thirdItem.css({zIndex:10}).show();
secondItem.css({zIndex:9}).hide();
firstItem.css({zIndex:8}).hide();
thirdItem.delay(3000).delay(0, function(){ transOne(); });
};
function transOne(){
console.log('transOne');
thirdItem.css({zIndex:9}).show();
secondItem.css({zIndex:8}).hide();
firstItem.css({zIndex:10}).hide();
firstItem.fadeIn("slow" , function(){ holdOne(); });
};
leadIn();
///toggle by clicking thumbnails
jQuery('#thumb-one').on('click', function(){console.log('1'); holdOne();});
jQuery('#thumb-two').on('click', function(){console.log('2'); holdTwo();});
jQuery('#thumb-three').on('click', function(){console.log('3'); holdThree();});
});
looks kinda complicated... all i understood was, that you are looking for a way to loop through a list of items AND interrupt the loop any time with a click and then start from there on...
i've needed something similar some time before... we start with the snippet from css-tricks.com
All he does, is using the setInterval function to cycle through the elements:
$("#slideshow > div:gt(0)").hide();
setInterval(function() {
$('#slideshow > div:first')
.fadeOut(1000)
.next()
.fadeIn(1000)
.end()
.appendTo('#slideshow');
}, 3000);
this is neat and nice. it shows the first element, fades it out, takes the next element, fades it in and finally appends the first element to the end of the list.
this means, that the visible element is always the first one and therefore good to address.
that was it from css-tricks ... now we are out on our own, and we do want to have a link list, to directly address the single slides... i choose to enumerate all my pages, so this was my solution:
for (i = 1; i <= $("#slides div").length; i++) {
var slideName = $("#slides div").eq(i - 1).attr("name");
$("#slideselect").append("<a href='#' class='singleSelect' name='" + slideName + "'>" + i + " </a>");
}
having a link-list, won't help, as long as you don't have a click function. since the links are inserted after document.ready, i had to use on("click", , function), to have the click handle available...
the function first of all finds the name of the clicked element and compares it to the current active element. in case they match, we won't do anything, since the clicked slide is already shown.
$("#slideselect").on("click", ".singleSelect", function () {
var clickedSlide = $(this).attr("name");
var activeSlide = $('#slides > .activeSlide').attr("name");
clearInterval(cycleHandle);
if (clickedSlide != activeSlide) {
then, because we don't want to destroy our ordered list, we loop through our elements always moving the first one to the last position, until we found our clicked element. afterwards, we fadeout the active slide and fade in the clicked one...
while (clickedSlide != $('#slides > div:first').attr("name")) {
$('#slides > div:first').appendTo('#slides');
}
$('#slides > .activeSlide').fadeOut(1000)
.removeClass("activeSlide").end();
$('#slides > div:first').fadeIn(1000)
.addClass("activeSlide")
.end();
}
cycleHandle = setInterval(function(){myCycle()}, 3000);
});
the code inside my post is already the finished version. what you may notice is the clearInterval and setInterval call... if you won't clear the interval, the clicked slide will correct fade in to display, but the interval keeps running with the old offset. e.g. if you click a page 1 sec before the interval-loop would move on, your clicked slide will be there for only 1sec...
so i took the css-trick snippet into my own cycle function, and call this once after document ready. and inside the click handle, you'll stop the interval and start it again...
well hope you understood my work... here's the fiddle: http://jsfiddle.net/sx1mozeg/2/
there is still strange behaviour with the transitions, if you spam your clicks... need to improve that ...
Related
I'm trying to set a loop on a slider but it would always stop on the last slider. Please help.
jsfiddle
function slideSwitch() {
var $active = $('div#slideshow .active');
var $next = $active.next();
$next.addClass('active');
$active.removeClass('active');
}
$(function() {
setInterval( "slideSwitch()", 5000 );
});
The problem is that you need to set $next to the first image when you reach the end. You can assume that if there is no $next elements, then it means that you need to start over from the beginning.
For example like this:
function slideSwitch() {
var $active = $('div#slideshow .active');
var $next = $active.next();
if (!$next.length) {
$next = $('div#slideshow > :first');
}
$next.addClass('active');
$active.removeClass('active');
}
$(function () {
setInterval(slideSwitch, 1000);
});
Unrelated, but instead of setInterval("slideSwitch()", 5000); pass function reference like in example above.
Demo: https://jsfiddle.net/na8zvhwh/2/
I would say that .next() make things needlessly complicated. This task is easily solved with some jQuery and a little math. Complete solution at the bottom, step-by-step with explanations following.
Pretty much only the internals of slideSwitch are changed. Requires that .active is set on one element. I like this solution because of its mathematical elegance, and because it's pretty easy to use once you understand the concept of it, and because it allows us to understand what we do instead of relying on things that "just work".
Step-by-step with explanation
What you want to do is get a list, iterate over it, change the class and then start from the beginning, right? A trick for this is using index % length.
Pretty much only the internals of slideSwitch are changed. Requires that .active is set on one element.
First, get the list of slides: (cache outside function for better performance)
var $list = $("div#slideshow");
Then, we want to find the active element and its index. This is done with:
var $current = $list.find(".active").index();
Then the magic. To find the next number, regardless of current position, we take $current, and add 1, then get the modulus (remained, essentially) of that number divided by the number of items total. The list of elements in #slideshow is returned by $list.children().
Thus, it will magically wrap around and start over when it reaches the end.
var $next = ($current + 1) % $list.children().length;
Then it's only a matter of removing the class from the current element and adding it to the next, using .get(index) which allows us to find() elements by their index and wrapping them in a jQuery object so we can use addClass() and removeClass(), because backwards compability.
$($list.children().get($current)).removeClass("active")
$($list.children().get($next)).addClass("active");
And that's it.
Full solution:
jsFiddle: http://jsfiddle.net/thn1vnmg/
function slideSwitch()
{
// Cache this outside function for performance
var $list = $("div#slideshow"),
$current = $list.find(".active").index(),
$next = ($current+1) % $list.children().length;
$($list.children().get($current)).removeClass("active")
$($list.children().get($next)).addClass("active");
}
$(function() {
setInterval( slideSwitch, 1000 );
});
http://en.wikipedia.org/wiki/Modular_arithmetic
A simple script that I use is this.
var imageShown = 1;
var total = 7;
function pic() {
var image = document.getElementById('picture');
imageShown = imageShown + x;
if(imageShown > total){imageShown = 1;}
if(imageShown < 1){imageShown = total;}
picture.src = "Images/img"+ imageShown +".jpg";
}
window.setInterval(pic, 5000);
You will need to customize the variables as needed to make your script match. Before this will work the pictures need to be put in the sub folder Images with the name img1, img2, and so forth. I know this will work because I have created an image slider that runs through automatically. Reset Javascript Timer for Image Slider
Im trying to create animation function where I click on lets say the last child in a list,
then I'll need to move all siblings one by one to the left. So it will go like a wave.
Example:
Click on child nr.4, sibling nr.1 starts to move to the left out of the screen, and with a short delay sibling nr.2 and so on follow after. So it will be like a wave effect.
I have created a JSFiddle: http://jsfiddle.net/10kjn00z/2/
$('#menu li').click(function(){
setVar(this);
$(this).siblings().animate({left: '-'+tWidth+'px'}, function() {
$(this).animate({top: '-'+onSet+'px'});
});
});
This fiddle is just a short snippet off my code, so there might be code thats isnt in use here. But I'll get the idea.
Thanks
You can use the setTimeout() function to achieve what you want.
Here's an example of how you can do it:
$('#menu li').click(function(){
var speed = 100;
setVar(this);
var siblings = $(this).siblings();
$.each( siblings, function(index,value){
setTimeout(function(){$(value).animate({left: '-'+tWidth+'px'});}, index*speed);
});
var current = this;
setTimeout(function(){$(current).animate({top: '-'+onSet+'px'})}, 400-speed+siblings.length*speed);
});
Check it out on jsFiddle
if all the elements to be shifted belong to the same parent:
$('menu li').click(function(event) {
var list = event.currentTarget.parentNode.children;
var i = list.length;
var timeout = 100
while (i--) {
setTimeout(function() {
$(list[i]).animate(/*logic here*/);
}, timeout);
timeout += 100;
}
})
This will iterate through all the children of the parent in reverse order and apply the animation. You can also tweak this to only call on certain siblings. If you want them to iterate in order, use the standard for loop instead of while. The value timeout corresponds to milliseconds of delay and you can adjust the initial and increment values to adjust the animation timing.
You can achieve that behaviour using jQuery.fn.delay, where the delay-time depends on the elements position in the siblings-list.
$('#menu li').click(function(){
setVar(this);
// call .each on siblings, because each will get a different delay
$(this).siblings().each(function(index, sibl) {
$(sibl).delay( index * 250 )
.animate({left: '-'+tWidth+'px'}, function() {
$(this).animate({top: '-'+onSet+'px'});
});
});
});
I'm trying to make a lightbox. But when i open the lightbox for the second time. It goes trough my code twice. When i open my lightbox the third time, it goes trough my code three times. Don't get it at all.
$(document).ready(function(){
$('.bg-overlay, .overlay-content, .overlay-content img').hide();
$('.thump-bnr > li').click(function(){
// show the overlay and bg-overlay
$('.bg-overlay, .overlay-content').fadeIn(500);
// gets the index of the thunp thats been clicked in the banner
var x = $(this).index();
// console.log(x);
$('.overlay-content > img').eq(x).fadeIn(500);
// thumpPop checks if there aren't to mutch list items
var thumpPop = $('.overlay-content .thump-pop li').length;
// console.log(thumpPop);
// appends all li for the thump navigation in the overlay
if (thumpPop < 1) {
$('.overlay-content').append('<ul class="thump-pop"></ul>');
for (var i = 0; i < 4; i++) {
$('.thump-pop').append('<li></li>');
}
}
// sets all thump li to the border white
$('.thump-pop > li').css("border-color", "#fff");
// sets the active thump li to a dark border
$('.thump-pop > li').eq(x).css("border-color", "#e2e2e2");
// console.log(x);
// calls the thumpNav function for the thump navigation
thumpNav();
// calles the arrowNav function for the arrow navigation beside the big images
arrowNav();
});
In this function i have managed to execute the function only once by using an if statement.
// this is the function for the thump navigation
function thumpNav() {
$('.thump-pop > li').click(function(){
// get the index number of the thump li
var y = $(this).index();
// console.log(y);
// checks if the big image thats equal to the clicked thump is hidden
if($('.overlay-content > img').eq(y).is(':hidden')) {
// fadeIn and fadeOut the big images
$('.overlay-content img').fadeOut();
$('.overlay-content > img').eq(y).fadeIn();
// this wil change the border color of the active li
$('.thump-pop > li').css("border-color", "#fff");
$(this).css("border-color", "#e2e2e2");
}
});
}
I think i have made a mistake in the function arrowNav(), because he executes this twice when i open my lightbox for the second time.
function arrowNav() {
$('.arrow-nav-left').click(function(){
// this wil get the index number of the visible image in the overlay. This number can be used to display the number -1 our +1
var x = $('.overlay-content').find('img:visible').index();
// console.log(x);
var x = x - 2;
console.log(x);
$('.overlay-content > img').hide();
$('.overlay-content > img').eq(x).show();
});
}
// hides the pop-up
$('.bg-overlay').click(function(){
$('.bg-overlay, .overlay-content, .overlay-content img').fadeOut(500);
});
});
Please help me, and some feedback on the code is alway helpfull. Thanks
The problem is here:
function thumpNav() {
$('.thump-pop > li').click(function(){
You're attaching a new click handler everytime you call thumpNav, and they will all execute and do the same thing everytime you click.
Replace with:
function thumpNav() {
$('.thump-pop > li').unbind("click").click(function(){
Just like you did with arrowNav().
Note that your code is very unefficient and not structured quite right. Even if this works it's not good when you're juggling click handlers like this. At least define the callback as a seperate function and pass that as an argument to click().
If you want to get help with improving your code, you can always post it on Codereview.
Every time you're calling:
thumpNav();
you're attaching a new click handler.
same with arrowNav()
but atleast here you unbind first.
I have a function that is fading in and out of four boxes that are in a row and then it loops through again and again. Box 1 fades in and out, then box 2, then box 3, and finally box 4. This is done in one function.
I have another function that when a user hovers over the heading of one of these boxes that box will fade in. Then when they hover off it fades out. What I want to do in that function is when they hover on one of the headings over the box, the function controlling the looping (startSlider) will fade out, then when they hover off the heading the looping begins again.
Here is some code:
function startSlider(){
//code that is looping through each box is here
};
function hoverHere(){
$('.headings .b1').on("mouseenter", function(){
$('.box #1').fadeIn(300);
//startslider() fade out this function
})
.on("mouseleave", function(){
$('.box #1').fadeOut(300);
//startslider() begins again
});
//there is the same code here for .b2 and .box #2 and so on
}
Thanks for any help on how to stop this function from looping when hover is in effect and then to start the startSlider() function when hover is off.
I've put everything in one function so all the variables will be
in scope.. also I'm using .index() which gives you the index of the element
in regards to its parent (a simpler way of connecting between the boxes clicked and the infoboxes affected.
To make the loop start right away, I've separated the actual sliding function from the looping interval, so you can call the function separately in the beginning and then start the loop.. Notice you only need to call startSlider() function in doc ready. Let me know if you have any issues.. If you rather do it the other way and only want the loop function to start immediately than you can just separate the sliding function as in the example.
function startSlider(){
// timer
var loop = 0;
// get total boxes
var count=$('.box .info').length;
// slide index
var sliderIndex = 0;
// boxes
var boxes = $(".headings").children("div");
// info boxes
var infoboxes = $("#main_cont").find(".info");
// bind boxes hover
boxes.off().on('mouseenter', handlehover);
function resetSlider() {
window.clearInterval(loop);
loop=window.setInterval(moveSlider, 2000);
}
function moveSlider() {
if(sliderIndex+1 == count){ //will reset to first image when last image fades out
sliderIndex = 0;
}
infoboxes.fadeOut(400);
infoboxes.eq(sliderIndex).fadeIn(400); // slider image + the next image in the slider
sliderIndex++;
}
function handlehover() {
var boxnum = $(this).index();
boxes.off().on('mouseleave', resetSlider);
pauseSlider();
}
function pauseSlider() {
window.clearInterval(loop);
infoboxes.not(":eq("+boxnum+")").fadeOut(400);
infoboxes.eq(boxnum).fadeIn(400);
}
}
$(function() {
startSlider();
});
i'm building a webpage where many spanĀ needs to be transitioned from one class to another to create a bg-color fadein effect. Distribution of elements of same classes is mixed through the page, but they are all grouped under common classes.
I want to create a behavior that does the following: when you click any elements of class-n, the other elements of that class transitions, with the clicked element acting as the starting point.
This is mostly figured out, thanks to some help on SO; see the jsfiddle.
$(".div").click(function () {
var itemClasses = this.classList;
var itemThread = itemClasses[1];
colorThread($(this), itemThread);
console.log(itemThread);
});
function colorThread($div, tId) {
tId = '.'+tId;
$div.toggleClass('div-clicked');
setTimeout(function () {
(function togglePrev($div) {
$div.toggleClass('div-clicked');
setTimeout(function () {
togglePrev($div.prev(tId));
}, 100);
})($div.prev(tId));
(function toggleNext($div) {
$div.toggleClass('div-clicked');
setTimeout(function () {
toggleNext($div.next(tId));
}, 100);
})($div.next(tId));
}, 100);
}
However, I am still struggling around a particular issue: I don't want the transition to stop if if encounter different class, I just want it not to toggle and keep iterating. If the jsfiddle, that would translate in all of the same color div to transition, regardless of their placement in the DOM tree.
In my togglePrev/toggleNext function, I have tried something along
if($div.hasClass(".classToTransition"))
{
$div.toggleClass(".div-clicked");
}
but couldn't get it to work properly (it doesn't ieterate to the next elements). There is something that I can't seem to grasp in the structure of that conditional. Anyone has a lead?
You really did manage to complicate something that should be pretty simple ?
$(".div").click(function () {
var coll = $('.'+this.className.replace(/(div-clicked|div)/g, '').trim()),
idx = coll.index($(this).toggleClass('div-clicked'));
$.each(coll, function(i) {
setTimeout(function() {
if (idx + i <= coll.length) coll.eq(idx + i).toggleClass('div-clicked');
if (idx - i >= 0) coll.eq(idx - i).toggleClass('div-clicked');
},i*200);
});
});
FIDDLE
It gets all the elements with the same class as the one currently clicked, and the index of the currently clicked, and then just adds and subtract 1 to the current index to get the next and previous elements. The checks are to make sure it stops when it reaches the end.
I don't want the transition to stop if if encounter different class, I just want it not to toggle and keep iterating
You might want to use nextAll(tId).first()/prevAll(tId).first() to select the next to-be-toggled element: http://jsfiddle.net/35uNW/4/. .next() does only look at the next sibling, and if that doesn't match your tId selector, no element will be selected.
If you want to iterate the different-classed elements so that you wait for each one, but don't want to toggle it, you can use your if-condition but you must remove the tId selector from the next()/prev() calls: http://jsfiddle.net/35uNW/3/.
This was a fun one. I did it a slightly different way, getting all of the matched elements and splitting them into before and after arrays.
var $allItems = $(".div");
$(".div").click(function () {
var itemClasses = this.classList;
var itemThread = itemClasses[1];
colorThread($(this), itemThread);
});
function colorThread($div, classname) {
var tId = '.'+classname,
$divs = $allItems.filter(tId),
index = $divs.index($div),
$before = $divs.slice(0, index),
before = $before.get().reverse(),
$after = $divs.slice(index+1);
$div.toggleClass('div-clicked');
$(before).each(function(i, item){
setTimeout(function () {
$(item).toggleClass('div-clicked');
}, i*100);
});
$($after).each(function(i, item){
setTimeout(function () {
$(item).toggleClass('div-clicked');
}, i*100);
});
}
Here's a working fiddle: http://jsfiddle.net/5sUr4/