SetTimeout inside a for loop - javascript

I'm trying to write a script that changes the z-index of 3 images. Basically the script should target the current image and apply a higher z-index on the next image, like a sort of carousel but with a z-index rather then active class. The challenge is to set the z-index after a specific interval. The problem is that the first image is displayed and then the last one. This is my code:
Html:
<div class="changingimages">
<img src="#" data-time="3000" width="100%" class="alternateimage alternateimage1">
<img src="#" data-time="2000" width="100%" class="alternateimage alternateimage2">
<img src="#" data-time="4000" width="100%" class="alternateimage alternateimage3">
</div>
jQuery Script
<script type="text/javascript">
jQuery(document).ready(function(){
var changeImg = function(i, time, currentImg) {
setTimeout(function(){
jQuery(currentImg).next().css("z-index", i);
}, time);
};
var numberOfChilds = jQuery(".changingimages").children().length;
var currentIndexClass;
var currentImg;
var time;
for (var i=1; i<=numberOfChilds; i++) {
currentIndexClass = '.alternateimage' + i;
currentImg = jQuery(currentIndexClass);
time = jQuery(currentIndexClass).attr("data-time");
changeImg(i, time, currentImg);
}
});
I think there is some problem with the closure inside a loop, but not sure!

It's a common misconception that setTimeout schedules events to run relative to previously queued events. It looks like you believe that, theoretically, the following:
setTimeout(f, 100);
setTimeout(g, 100);
setTimeout(h, 100);
would result in a timeline like this:
0ms Start
100ms Run f()
200ms Run g()
300ms Run h()
The reality is that the time option in setTimeout means "run this function after at least this much time has passed." Going off of the previous example, you would actually get something more like
0ms Start
100ms Run f()
101ms Run g()
102ms Run h()
To space out your code correctly, keep adding to the timeout time rather than replacing it.
var time = 0;
for (var i = 1; i <= numberOfChilds; i++) {
currentIndexClass = '.alternateimage' + i;
currentImg = jQuery(currentIndexClass);
// Add to the previous time
time += parseInt(jQuery(currentIndexClass).attr("data-time"), 10);
changeImg(i, time, currentImg);
}

Here is a fiddle implementing the use of timeout to achieve what you want.
fiddle
.textArea {
position: absolute;
height: 50px;
width: 50px;
display: block;
}
.box_a {
background-color: blue;
}
.box_b {
background-color: red;
}
.box_c {
background-color: orange;
}
.active {
z-index: 3;
}
<div class="textArea box_a active">a</div>
<div class="textArea box_b">b</div>
<div class="textArea box_c">c</div>
$(function(){
var $elem = $('.textArea');
timeout(0);
function timeout(i){
$($elem[i]).addClass('active');
return setTimeout(function(){
$elem.removeClass('active');
i++;
if(i >= $elem.length){
i = 0
}
timeout(i);
}, 1000)
}
});
Note it does not use a for loop, because timeout is asynchronous and will not execute sequentially. Each timeout will fire at the same time basically, then do their action based on the wait time.
The solution is to make a function that keeps track of the index, and when the last timeout has completed execution.

Related

Rotating several images with different timings

I have three images side by side, left, middle and right. I want the first image on the left to change after 2 seconds, then the one in the middle to change 2 seconds later and then the one on the right to change 2 seconds after that. Then after another 2 seconds I want the first one on the left to change again and for the sequence to start all over again.
I've put together the javascript code for each image to have a certain start time and then a 6 second interval before changing again, this gives the effect I'm looking for.
The sequence works the first time round but when the first image is due to run through the sequence the second time round the whole thing seems to stick a bit and then all the images start changing together, as if they are all affecting one another. I don't know why this is since the code refers to each separately. Any help would be appreciated. Here's the code:
HTML Code:
<div>
<img id="mainImage" src="firstimage.jpg">
<img id="mainImage1" src="secondimage.jpg">
<img id="mainImage2" src="thirdimage.jpg">
</div>
Javascript Code:
<script>
var myImage = document.getElementById("mainImage");
var imageArray = ["image1.jpg","image2.jpg","image3.jpg"];
var imageIndex = 0;
function changeImage() {
myImage.setAttribute("src",imageArray[imageIndex]);
imageIndex++;
if (imageIndex >= imageArray.length) {
imageIndex = 0;
}
}
setTimeout(changeImage, 0000);
setInterval(changeImage,6000);
</script>
<script>
var myImage1 = document.getElementById("mainImage1");
var imageArray1 = ["image4.jpg","image5.jpg","image6.jpg"];
var imageIndex1 = 0;
function changeImage1() {
myImage1.setAttribute("src",imageArray1[imageIndex1]);
imageIndex1++;
if (imageIndex1 >= imageArray1.length) {
imageIndex1 = 0;
}
}
setTimeout(changeImage1, 2000);
setInterval(changeImage1,6000);
</script>
<script>
var myImage2 = document.getElementById("mainImage2");
var imageArray2 = ["image7.jpg","image8.jpg","image9.jpg"];
var imageIndex2 = 0;
function changeImage2() {
myImage2.setAttribute("src",imageArray2[imageIndex2]);
imageIndex2++;
if (imageIndex2 >= imageArray2.length) {
imageIndex2 = 0;
}
}
setTimeout(changeImage2, 4000);
setInterval(changeImage2,6000);
</script>
Solution:
For each
setTimeout(changeImage2, x);
setInterval(changeImage2,6000);
change to
setTimeout(function() {
setInterval(changeImage2,6000);
}, x);
Check here: https://jsfiddle.net/bstd3fqu/4/
Explanation:
setTimeout() doesn't make runtime to sleep for certain time. It simply set a timer to execute the mentioned function after certain time. So all setInterval() calls are executing at almost same time in your implementation. I am just setting the interval in the setTimeout function so that these setInterval() calls are executing at different times.

jQuery .each() method doesn't iterate over all images

I am trying to make an image carousel. I am using the jQuery .each() method to iterate over all the images in the div with class="slideshow".
HTML code
<div class="slideshow">
<img src="images/page-1-hero-image.jpg" alt="school's image" class="img-responsive page-one-pic mySlides">
<img src="images/Capture2.PNG" alt="school pic" class="img-responsive mySlides">
<img src="images/Capture.PNG" alt="school pic" class="img-responsive mySlides">
<img src="images/Capture3.PNG" alt="school pic" class="img-responsive mySlides">
</div>
CSS code
.mySlides {
display: none;
position: fixed;
left: 0;
top: 0;
z-index: 1;
/* to make pic responsive */
min-height: 100%;
min-width: 1024px;
width: 100%;
height: auto;
}
Javascript:
function carousel() {
$(".slideshow > img").each(function(index, element) {
$(element).fadeIn(1000).delay(2000);
setTimeout(carousel, 1000);
});
}
The function only fades in the first image and then stops. The other images are not displayed.
here is the link to the hosted project:
https://rimildeyjsr.github.io/St.Anthony-Website/
This code:
function carousel() {
$(".slideshow > img").each(function(index,element){
$(element).fadeIn(1000).delay(2000);
setTimeout(carousel,1000);
});
}
Says: "For each img element, spend a second fading in, then delay by two seconds before doing nothing, and reschedule this entire process (not per element, for all of them) to run again a second from now."
That doesn't make much sense, you'll be calling carousel several times again roughly at the time the first image finishes fading out.
Given the term "slideshow" I'd guess what you're trying to do is show each image for two seconds before spending a second having the next one fade in, looping when you get to the end. If so, you want to call carousel once, two seconds after the last image has finished fading in. You can do that by delaying the fades and the next call.
function carousel() {
var imgs = $(".slideshow > img");
imgs.each(function(index,element){
// Don't make the first one (index = 0) wait at all;
// make the second (index = 1) wait 3 seconds, the third
// (index = 2) wait 6 seconds, etc. And then fade in
$(element).delay(index * 3000).fadeIn(1000);
});
// Start the entire process again two seconds after the last image fades in
setTimeout(carousel, imgs.length * 3000);
}
Just an improvement to the previous answer, you can do it in the form of a callback(which ensures you that the function is called only after the fadeIn has occurred or happened, so you wont need a setTimeout) as follows
function carousel() {
var imgs = $(".slideshow > img");
imgs.each(function(index, element) {
$(element).delay(index * 2000).fadeIn(1000, function() {
if (index + 1 >= imgs.length) {
carousel(); // call the function only after all images are fadeIn completed
}
});
});
}
See if that helps and if not drop a comment below
Edit:
After .fadeIn() does it's job, it sets the display value to block what you have to do here is, hide the elements before continuing the slideshow animation, for simplicity we'll set all the img elements display to hidden by using .hide(0). This sets the elements display to none
function carousel() {
var imgs = $(".slideshow > img");
imgs.stop().hide(0); // hide all the images whenever the carousel() is called
imgs.each(function(index, element) {
$(element).delay(index * 2000).fadeIn(1000, function() {
if (index + 1 >= imgs.length) {
carousel(); // call the function only after all images are fadeIn completed
}
});
});
}
Let me know if you need anything else

Spinner radial color bar

I would like to create effect like this video: http://youtube.com/watch?v=bEVjLfq9lvk
I want to create spinner color with text in center and when color bar are full completed (100%) adding some event.
I think it's more simple use a plugin but I don't anyone and it should works with Ionic. How can I create this module for my app?
Okay so since you wanted to have a time bar, I made something myself some time ago. Be aware that this solution isn't really the best, since there are rounding errors which makes the bar become slightly too long or too short instead of hitting exact 100% depending on its width. I'm sure there are better solutions, but this is what I got right now:
https://jsfiddle.net/a6wogez6/1/
HTML:
<div id="timebg">
</div>
<div id="time">
</div>
CSS:
#timebg{
height: 20px;
width: 300px;
background: #cc0000;
}
#time{
margin-top: -20px;
height: 20px;
width: 0px;
background: #00cc00;
}
Javascript:
var timerrunning;
var width;
var currentwidth;
var intervaltime;
function init() {
timerrunning = false;
width = document.getElementById("timebg").offsetWidth;
currentwidth = 0;
// interval steps to increase the timing bar by 3px
// 5 Seconds/(width of the bar) * 3 for 3px per step
intervaltime = Math.floor(5000/Math.floor(width))*3;
}
function gameStart(){
// your game starts, you start the timer
startTimer();
// timer times out after 5 seconds, the bar will be at 100%
timeout = setTimeout(function () {
stopTimer();
// do something here like gameOver()
}, 5000);
}
// function which starts the timer
function startTimer(){
timerrunning = true;
timer = setInterval(function(){loop()}, intervaltime);
}
// function which stops the timer and your timeout = level completed before time ran out
function stopTimer(){
timerrunning = false;
clearInterval(timer);
}
// the function which increases the width of the timing bar
function loop(){
currentwidth = currentwidth + 3;
css( '#time', 'width', currentwidth + 'px');
}
// some css selecting function I found somewhere...
function css(selector, property, value) {
for (var i=0; i<document.styleSheets.length;i++) {//Loop through all styles
//Try add rule
try { document.styleSheets[i].insertRule(selector+ ' {'+property+':'+value+'}', document.styleSheets[i].cssRules.length);
} catch(err) {try { document.styleSheets[i].addRule(selector, property+':'+value);} catch(err) {}}//IE
}
}

How do I make this fade in/out infinite?

this code fades in and fades out the div #shape while the start variable is true.
when i call the "start" method from event "click" the browser stops working because the while inside method is infinitive and "click" event does not finish until "start" method is done.
i want the method to run after the "click" event is finished.
what should i do?
CSS
#shape {
background-color:red;
width:100px;
height:100px;
display:none;
}
HTML
<div id="shape"></div>
<button id="startButton">start game!</button>
JS
var start = false;
$("#startButon").click(function () {
start = true;
startGame();
});
function startGame() {
while (start == true) {
$("#shape").fadeIn(1000).delay(1000).fadeOut(1000);
}
}
You don't need the flag, just make a recursive function. I changed the time to 300 milliseconds so you can see it easier
http://jsfiddle.net/zfbptz9c/
$("#startButton").click(function () {
startGame();
});
function startGame() {
$("#shape").fadeIn(300, function () {
$("#shape").fadeOut(300, function () {
startGame();
});
});
}
The div will fade in and on complete of the fade in, it will fade out then call the startGame function again and the entire process will repeat infinitely.
Alternatively, this can be achieved with css only if you only need to target modern browsers. I will put this fiddle link here, it is from a different question. I won't paste the code since you did not tag the question with css but the fiddle shows everything. I take no credit for it.
How can I create a looping fade-in/out image effect using CSS 3 transitions?
http://jsfiddle.net/FTLJA/261/
JavaScript runs in a single threaded environment, meaning once you enter an infinite loop, you can only quit the loop from within it. In synchronous execution, like the one you have here, no code outside the loop can affect the loop condition.
As far as your problem, people suggested solutions such as making a recursive function or using CSS3 transitions.
Another possible way could be to use timing functions like setTimeout and/or setInterval
The code bellow will make the fadeIn/Out happen after every second, after start button is clicked and until stop button is clicked.
var toggle = true; // flag for animation direction
var shape = $("#shape"); // so we don't select the shape each time we animate
var duration = 1000; // animation duration
var delay = 1000; // delay between animations
var timerId; // timer id returned by setInterval
// start animating the shape after the delay
$("#startButton").click(function() {
timerId = setInterval(animate, delay);
});
// stop animating the shape and hide it
$("#stopButton").click(function() {
clearInterval(timerId);
shape.css('display', 'none');
});
// function that animates the shape depending on the toggle flag
function animate() {
if (toggle) {
shape.fadeIn(duration);
toggle = false;
} else {
shape.fadeOut(duration);
toggle = true;
}
}
#shape {
background-color: red;
width: 100px;
height: 100px;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="shape"></div>
<button id="startButton">start game!</button>
<button id="stopButton">stop game!</button>

Loop with trigger (which contains animation) not working

So I seem to have run into a bit of a dead end. I'm making a page which has an image slider. The slider has three images, one centered on the screen, the other two overflow on the left and right. When you click on the button to advance the slides it runs this code....
$('#slideRight').click(function() {
if ($('.container').is(':animated')) {return false;}
var next=parseInt($('.container img:last-of-type').attr('id')) + 1;
if (next == 12) {
next = 0;
}
var diff = galsize() - 700;
if ($('.thumbs').css("left") == "0px") {
var plus = 78;
} else {
var plus = 0;
}
var app='<img id="' + next + '" src="' + imgs[next].src + '">';
$('.container').width('2800px').append(app);
$('.container').animate({marginLeft: (diff + plus) + "px"}, 300, function() {
$('.container img:first-of-type').remove();
$('.container').width('2100px').css("margin-left", (galsize() + plus) + "px");
});
}); // end right click
This works just fine, not a problem..... I also have an interval set up to run this automatically every 5 seconds to form a slideshow...
var slideShow = setInterval(function() {
$('#slideRight').trigger("click");
}, 5000);
This also works perfectly, not a problem.... However my problem is this.... I have thumbnails, when you click on a thumbnail, it should run this code until the current picture is the same as the thumbnail.... here is the code....
$('img.thumbnail').click(function() {
clearInterval(slideShow);
var src = $(this).attr("src");
while ($('.container img:eq(1)').attr('src') != src) {
$('#slideRight').trigger("click");
}
});
When I click on the thumbnail nothing happens... I've used alert statements to try and debug, what ends up happening is this....
The while loop executes, however nothing happens the first time. The slide is not advanced at all. Starting with the second execution, the is('::animated') is triggered EVERY TIME and the remainder of the slideRight event is not executed...
So my first problem, can anyone shed some light on why it doesn't run the first time?
And my second question, is there any way to wait until the animation is complete before continuing with the loop?
Any help would be appreciated. Thank you!
I'm going to start with the second part of your question, regarding completing the animation before continuing with the loop.
I have done something similar in the past, and what I did was set two global variables to control the animation. One variable is for how long you want the period to be, the other is a counter for how much time since the last loop.
So, for example:
$timeToChange = 5; // in Seconds
$timeSinceReset = 0; // also in Seconds
Set your interval for one second and call a new function (autoAdvance()):
var slideShow = setInterval(function() {
autoAdvance();
}, 1000); // only one second
and then use the counter variable to count each time the interval is called (each second). Something like:
function autoAdvance(){
if($timeSinceReset == $timeToChange){
$timeSinceReset = 0;
$('#slideRight').trigger("click"); // execute click if satisfied
}
else{$timeSinceReset++;}
}
To stop from looping until the animation is done, reset $timeSinceReset back to 0 (zero) when you click on the thumbnail. Something like:
$('#thumbnail').click(function(){
$timeSinceReset = 0;
});
That'll give you a nice 5 second buffer (or whatever you set $timeToChange) before the loop continues.
As for the first part of your question, grab the number of the particular thumbnail, and use that to scroll to the appropriate image. Something like:
$('.thumb').click(function (each) {
$childNumber = $(this).index();
});
which you cansee in this fiddle. Click in one of the grey boxes and it'll tell you which one you clicked in. Use that info to scroll to the appropriate image (1, 2 or 3 if you only have three images).
Hope this helps.
Here is a full solution for one possible way of doing it at this fiddle.
HTML:
The top container holds the images. In this particular example I've included three, using divs instead of images. Whether you use images or divs doesn't change anything.
<div class="holder_container">
<div class="img_container">
<div class="photo type1">ONE</div>
<div class="photo type2">TWO</div>
<div class="photo type3">THREE</div>
</div>
</div>
.img_container holds all the images, and is the same width as the sum of the widths of the images. In this case, each image (.photo) is 150px wide and 50px tall, so .img_container is 450px wide and 50px tall. .holder_container is the same dimensions as a single image. When this runs, the .holder_container is set to hide any overflow while .img_container moves its position left or right.
Included also are two nav buttons (forward and back)
<div class="nav_buttons">
<div class="nav back"><<<</div>
<div class="nav forward">>>></div>
</div>
As well as three thumbnail images - one for each image in the top container
<div class="top">
<div class="thumb"></div>
<div class="thumb"></div>
<div class="thumb"></div>
</div>
CSS:
Refer to the JS Fiddle for all CSS rules.
The most important are:
.holder_container {
margin: 0px;
padding: 0px;
height: 50px;
width: 150px;
position: relative;
overflow: hidden;
}
.img_container {
margin: 0px;
padding: 0px;
height: 50px;
width: 450px;
position: relative;
left: 0px;
top: 0px;
}
In the example, .type1, .type2 and .type3 are only used to color the image divs so you can see the animation. They can be left out of your code.
JavaScript:
The javascript contains the following elements…
Variables:
var timeToChange = 3; // period of the image change, in seconds
var timeSinceReset = 0; // how many seconds have passed since last image change
var currentImage = 1; // Which image you are currently viewing
var totalImages = 3; // How many images there are in total
Functions:
autoAdvance - called once every second via setInterval. Counts the number of seconds since the last image change. If the number of seconds that has passed is equal to the period, a function is called that switches the images.
function autoAdvance() {
if (timeSinceReset == timeToChange) {
timeSinceReset = 0;
moveNext();
} else {
timeSinceReset++;
}
}
moveNext() - moves to the next image. If the current image is the last (currentImage == totalImages) then currentImages is set back to 1 and the first image is displayed.
function moveNext(){
if(currentImage == totalImages){
currentImage = 1;
var newPos = 0 + 'px';
$('.img_container').animate({left: newPos}, 300);
}else{
currentImage++;
var newPos = -((currentImage-1) * 150) + 'px'; // child numbers are zero-based
$('.img_container').animate({left: newPos}, 300);
}
}
Rest of code:
If one of the thumbs is clicked, move to the corresponding image.
$('.thumb').click(function (each) {
timeSinceReset = 0;
var childNumber = $(this).index();
currentImage = childNumber + 1; // child numbers are zero-based
var newPos = -(childNumber * 150) + 'px'; // child numbers are zero-based
$('.img_container').animate({left: newPos}, 300);
});
If one of the navigation buttons is clicked, move accordingly. If "back" is clicked, move one image backwards (or to last image if currently on first). If "first" is clicked, move one image forwards (or to first image if currently on last).
$('.nav').click(function(){
timeSinceReset = 0;
if($(this).hasClass('back')){ // Back button
if(currentImage == 1){
currentImage = totalImages;
}else{
currentImage--;
}
}else{ // Forward button
if(currentImage == totalImages){
currentImage = 1;
}else{
currentImage++;
}
}
var newPos = -((currentImage-1) * 150) + 'px';
$('.img_container').animate({left: newPos}, 300);
});
Here is the link to the fiddle example.

Categories

Resources