Animate siblings one by one with delay - javascript

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'});
});
});
});

Related

Setting loop on a slider

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

jQuery making a loop of functions, each with an animate queue

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 ...

Wrap every child divisible by 5 and previous 4 in a div

I've been working on this JSFiddle to practice my understanding of jquery, but now I'm stuck.
How do you wrap a child element in a div to follow this pattern: child elements 1-5, then child elements 6-10, then child elements 11-15, and so on?
I'm working on a tally counter, so I want every 5 tallies to cluster together. That way, I can more easily select the last child and apply a class to make it rotate, in order to "cross out" the previous 4 tallies.
edit: (To clarify: I've been looking into selecting by index and by nth-child/nth-of-type, but those methods can only really grab the fifth element, or maybe even multiples of five? It doesn't grab the previous divs, too.)
edit 2: (So, you can actually use those selectors! I figured I was getting something wrong. It's always something simple.)
$(".button").click(function() {
var $button = $(this);
var oldValue = $button.parent().find("input").val();
if ($button.text() == "+") {
var newVal = parseFloat(oldValue) + 1;
} else {
// Don't allow decrementing below zero
if (oldValue > 0) {
var newVal = parseFloat(oldValue) - 1;
} else {
newVal = 0;
}
}
$("#counternumber").val(newVal);
});
$("#plus").click(function() {
var tally = "<div class='tally'>I</div>";
$("#dummy").append(tally)
});
$(function(){
$('#scratchpad.tally:nth-of-type(5)').wrap('tallyfamily');
});
JSFiddle.
Here is a general solution to wrap elements in groups of 5:
$(".holder > div:nth-child(5n-4)")
.addClass("first-of-group")
.each(function(){
$(this).nextUntil(".first-of-group")
.addBack()
.wrapAll("<div class='wrapper'>");
})
.removeClass("first-of-group");
http://jsfiddle.net/nJJM8/1/
Basically, :nth-child(5n-4) gets the first element in each group of 5. Then a class is temporarily added to keep track of these. nextUntil is used to find all elements up until the next element with that class. And finally wrapAll is used to wrap the matched elements in a div.
EDIT: Even easier:
var $divs = $(".holder > div");
for (var i = 0; i < $divs.length; i += 5) {
$divs.slice(i, i + 5).wrapAll("<div class='wrapper'>");
}
http://jsfiddle.net/kMzeN/1/
You're almost there, but a couple of things to note. You will only call your "wrap" function once, as it's outside of the click event. If you are dynamically adding, then you'll want to call it each time.
Secondly, with the HTML in your fiddle, you will never get the 5th record because you are appending your selector is looking for the 5th element with ID "scratchpad" with the class of tally. You'd need to change your selector to something that looks for all tallies, like so:
$(".tally:nth-of-type(5)").css('color', 'red');
I've updated the fiddle you were working on, and my code highlights each 5th record, so you can see what's going on. You were close, but you'll also want to add to your "nth-of-type" selector the use of "n", this way it gets every 5th record, not just the 5th one. So the full function becomes this
$("#plus").click(function() {
var tally = "<div class='tally'>I</div>";
$("#dummy").append(tally);
$(".tally:nth-of-type(5n)").css('color', 'red');
});
Fiddle: http://jsfiddle.net/Hfz9L/16/
To rotate (or apply any other property) to each 5th element, you don't even need to wrap them. Just specify a css class using the nth-of-type(5n) and it will affect every 5th element.
#scratchpad .tally:nth-of-type(5n) {
display: inline-block;
transform:rotate(20deg);
-ms-transform:rotate(20deg); /* IE 9 */
-webkit-transform:rotate(20deg); /* Opera, Chrome, and Safari */
}
Here is your fiddle updated: http://jsfiddle.net/Hfz9L/20/
Check this Working Demo Fiddle
$("#plus").click(function() {
var tally = "<div class='tally'>I</div>";
$("#dummy").append(tally);
$('#scratchpad .tally:nth-of-type(5n+1)').prevUntil('span').wrapAll('<span style="margin-right:5px;color:red;text-decoration:line-through;"></span>');
});
$('#scratchpad .tally:nth-of-type(5n+1)').prevUntil('span').wrapAll('<span style="margin-right:5px;color:red;text-decoration:line-through;"></span>');
Some changes:
$('#scratchpad .tally:nth-of-type(5n+1)') and not $('#scratchpad.tally:nth-of-type(5)'). - .tally is the child of #scratchpad ; selector to be used :nth-of-type(5n+1)
Use .wrapAll() - to wrap the selected elements in a <span> or any other element.
.prevUntil() - get all the previous elements.
You can make a for loop and do this:
for(i=1;i<=noOfChildElements/5;i++)
{
$('.child:nth-child('+i+'), .child:nth-child('+(i+1)+'), .child:nth-child('+(i+2)+'), .child:nth-child('+(i+3)+'), .child:nth-child('+(i+4)+')').wrapAll("<div />");
}
Basically I'm going through the child elements in the for loop and at every turn of the loop I'm selecting the 5 next child elements and wrapping them in a div using the .wrapAll() function. Hope this helps.

jQuery iteration though all items of a class regardless of their position in the DOM

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/

JQuery If Statement using each value from an array

I am writing a function that will be executed on multiple views of an application, and each view can have up to 50 instances of the same element: '.console'. I need to be able to perform an action every time the viewport scrolls to each instance. I have the following code setting up the variables:
//Create empty array with variable values, up to 50
var console = [];
//Find each instance of ".console" and populate the array with its pixel position.
$('.console').each(function() {
console.push($(this)[0].offsetTop);
});
//Determine the current pixel position of the scroll
var scroll = $(document).scrollTop();
Those variables all work fine and dandy, but after hours of pouring over jquery docs I can't figure the if statement out. Here is what I have that works well for the first item in the array:
if (scroll == console[0]){
$('.container').show();
} else {
$('.container').hide();
}
However, I want it to be anytime the scroll position matches each of the values in that array, hopefully something like this:
if (scroll == console[0-50])
Here is the full chunk as is:
$(document).on('scroll', function(){
//Create empty array with variable values, up to 50
var console = [];
//Find each instance of ".console" and populate the array with its pixel position.
$('.console').each(function() {
console.push($(this)[0].offsetTop);
});
//Determine the current pixel position of the scroll
var scroll = $(document).scrollTop();
//Anytime the scroll matches any of the instances of console, show a div
if (scroll == console[0]){
$('.container').show();
} else {
$('.container').hide();
}
});
Any help would be appreciated. I am pretty new to Javascript/JQuery so if I'm approaching the problem in the wrong way altogether, please let me know. Thanks!
Since you said it works for the first one, I'm guessing this may work.
// cache the container
var container = $('.container');
$(document).on('scroll', function(){
//Determine the current pixel position of the scroll
var scroll = $(document).scrollTop();
//Create empty array with variable values, up to 50
var console = [];
//Find each instance of ".console" and populate the array with its pixel position.
$('.console').each(function(index) {
console.push($(this)[0].offsetTop);
if (scroll == console[index]){
$(container).show();
} else {
$(container).hide();
}
});
});
You may wish to take a look at Waypoints. It's a jQuery plugin that is well suited for what you're trying to accomplish.
I whipped up a quick jsFiddle to show it in action: http://jsfiddle.net/dmillz/4xqMb/
$(".console").waypoint(function(direction) {
// Hide or show your ".container" object
});
More Waypoint examples: http://imakewebthings.com/jquery-waypoints/#get-started
Hopefully I understand your problem, which is as follows:
You have a bunch of elements with the .console class, and you want to appear as soon as they are in the viewport. When these elements aren't in the viewport you want them to dissapear?
Since you're interested in when these objects with the .console class are in the viewport, I suggest using this jQuery plugin
http://plugins.jquery.com/appear/
https://github.com/morr/jquery.appear
I suggest wrapping each of the .console objects in a container with another class, and then as these containers appear and disappear show and hide them.
At document ready just do the following:
$(document).ready(function() {
$('<.container-class>').appear();
$('<.container-class>').on('appear', function() { $(this).find('.console').show(); });
$('<.container-class>').on('disappear', function() { $(this).find('.console').hide(); });
});
To answer the question, you could do this:
var cons = $.map($('.console'), function(el) {
return $(el).offset().top;
});
$(document).on('scroll', function(){
var scroll = $(window).scrollTop();
$('.container').toggle( $.inArray(scroll, cons) != -1 );
});
But creating something for a range, considering the height of each element, the height of the window etc. would be a lot more involved.
While the problem was solved via another answer, figuring out how to perform a loop for each value in the array wasn't really solved ... UNTIL NOW!
This is probably a really gross and bloated way to do it, but if you essentially count how many items are in the array, you can then run a loop that many times, putting in the index for each value in the array. Code below:
//Create empty array with variable values
var console = [];
//Find each instance of ".console" and populate the array with its pixel position.
$('.console').each(function() {
console.push($(this)[0].offsetTop);
});
//Count the number of items in the array
var consoleIndex = console.length - 1;
$(document).on('scroll', function(){
//Determine the current pixel position of the scroll
var scroll = $(document).scrollTop();
//Anytime the scroll matches any of the instances of console, show a div
for (var i = 0; i <= consoleIndex; i++) {
if (scroll = console[i]) {
$('.container').toggle();
}
}
});

Categories

Resources