setTimeout function not working with for loop - javascript

I'm trying to run this code to make a slideshow of 3 images, but it's not working properly. The slideshow runs once, but then just stops...
Below is my JavaScript, CSS and HTML, and if you run the code snippet you will see the slide show run once.
The HTML is basically declaring a container div with the slides as divs wth a specific background image. (For this question I changed the images to colors so you can see the slideshow in action)
The CSS declares the position of each of the images of the slideshow when it is in plain view and when it is hidden.
The JavaScript cycles through the slides giving each one a class of "hidden" when they are supposed to move out of view. At the end, it brings back the first slide by removing all the "hidden" classes.
I'm pretty sure the issue is in the JavaScript with the setTimeout function since whenever I run the functions in the console it works perfectly, but when I try to automatically set them to run in my JS it only runs once.
function slide1to2(){
document.getElementById('slide1').className = "hidden";
clearTimeout(s1to2);
}
function slide2to3(){
document.getElementById('slide2').className = "hidden";
clearTimeout(s2to3);
}
function slide3to1(){
document.getElementById('slide1').className = "";
document.getElementById('slide2').className = "";
clearTimeout(s3to1);
}
for(i=0; i<100; i++){
s1to2 = window.setTimeout(slide1to2, 2000);
s2to3 = window.setTimeout(slide2to3, 4000);
s3to1 = window.setTimeout(slide3to1, 6000);
}
div#slideshow{
width:100%;
height:50%;
background-image:url("white-wall.png");
}
div#slideshow div{
width:100%;
height:inherit;
background-repeat:no-repeat;
background-position:center;
background-color:rgba(0,0,0,0.5);
position:absolute;
transition:1s;
}
div#slideshow div[id$="1"]{
background:#FF8888;
transform:translateX(0%);
z-index:3;
}
div#slideshow div[id$="2"]{
background:#88FF88;
transform:translateX(0%);
z-index:2;
}
div#slideshow div[id$="3"]{
background:#8888FF;
transform:translateX(0%);
z-index:1;
}
div#slideshow div[id$="1"].hidden{
transform:translateX(-100%);
}
div#slideshow div[id$="2"].hidden{
transform:translateX(-100%);
}
<div id="slideshow">
<div id="slide1"></div>
<div id="slide2"></div>
<div id="slide3"></div>
</div>

When you setTimeout(), the code doesn't wait for the timeout to finish. That means that your for loop is executed 100 times after 2 seconds, another 100 times after 4 seconds, and another 100 times after 6 seconds.
Instead of that, you can re-set the timeouts after the third frame is finished.
An example of simple loop with timeouts:
var i = 0;
function a() {
//Do some stuff
if (i++ < 100) setTimeout(a, 1000);
}
This ^ is good. A bad example would be:
for (var i = 0; i < 100; i++) {
setTimeout(function() {
//Do stuff
}, 1000);
}
The first will execute 100 times with interval of 1 second, the latter will execute 1 time.

You could get same effect without using JS with CSS3 animations (not exactly the same code, you should play with the times and the styles a little bit)
https://plnkr.co/edit/2AtRK7z9OJhs4AwX82rx?p=preview
.animation--slideshowAnimation {
width: 100%;
height: 500px;
background-size: cover;
background-position: 50%;
background-repeat: no-repeat;
animation-duration: 12s;
animation-name: slideshowAnimation;
animation-delay: 0;
animation-iteration-count: infinite;
animation-direction: forward;
}

I got it working with this:
(Thanks Yotam Salmon for your answer, thats what helped me understand what I was doing wrong)
I basically got each function to set the timeout and run the following one, then i got the last one to run the first one again. all i needed then was to start the first one manually then it just ran forever.
function slide1to2(){
document.getElementById('slide1').className = "hidden";
setTimeout(slide2to3, 4000);
}
function slide2to3(){
document.getElementById('slide2').className = "hidden";
setTimeout(slide3to1, 4000);
}
function slide3to1(){
document.getElementById('slide1').className = "";
document.getElementById('slide2').className = "";
setTimeout(slide1to2, 4000);
}
slide1to2();
div#slideshow{
width:100%;
height:50%;
background-image:url("white-wall.png");
}
div#slideshow div{
width:100%;
height:inherit;
background-repeat:no-repeat;
background-position:center;
background-color:rgba(0,0,0,0.5);
position:absolute;
transition:1s;
}
div#slideshow div[id$="1"]{
background:#FF8888;
transform:translateX(0%);
z-index:3;
}
div#slideshow div[id$="2"]{
background:#88FF88;
transform:translateX(0%);
z-index:2;
}
div#slideshow div[id$="3"]{
background:#8888FF;
transform:translateX(0%);
z-index:1;
}
div#slideshow div[id$="1"].hidden{
transform:translateX(-100%);
}
div#slideshow div[id$="2"].hidden{
transform:translateX(-100%);
}
<div id="slideshow">
<div id="slide1"></div>
<div id="slide2"></div>
<div id="slide3"></div>
</div>

As others have mentioned, your loop effectively does nothing, as setTimeout is asynchronous.
One way to achieve the result you want is to set the callback function in each one. Then set another function which will clear the timeouts. To loop forever, just remove the stopTransitions function and its caller.
https://jsfiddle.net/24aLyg5v/
var timeout1, timeout2;
function slide1to2(){
document.getElementById('slide1').className = "hidden";
document.getElementById('slide2').className = "";
timeout1 = window.setTimeout(slide2to1, 2500);
}
function slide2to1(){
document.getElementById('slide2').className = "hidden";
document.getElementById('slide1').className = "";
timeout2 = window.setTimeout(slide1to2, 2500);
}
function stopTransitions() {
clearTimeout(timeout1);
clearTimeout(timeout2);
}
window.setTimeout(stopTransitions, 20000);
slide1to2();

Related

Changing content inside single div with images on slider

I've tried many codes from multiple answers around internet, but none actually worked..
If images on slider started swapping content inside single .div, the slider didn't work or was collapsing..
I've succeed once, but then changed single thing and when I went back with ctrl+z, it didn't work back again. That got me so angry and hopeless, that I decided to write here.
So this is my 'precious' slider in a snippet. I want single image to be clickable and display different content, which would appear in a .div below, and make other content dissapear.
Side note: When I used this solution with jquery jQuery changing content inside Div
It made the slider once work, then I added 1 line, then went back, and at once it was not working. Which was really strange - this never happened to me, that the code was the same, but didn't work second time. The possible code for .div content swapping, must've been compatible with the slider.
I am a newbie and know nothing about jquery or javascript, so please be considerate!-->
Peek of my website with working slider - same code as below. As I said, I'd want images to be clickable, and change content inside a .div, that would be settled under slider. Each image is different content
"use strict";
var SLIDER_CLASS = 'slider';
var DELAY = 4000;
var sliders = document.getElementsByClassName(SLIDER_CLASS);
initSliders();
function slideAll () {
for (var i=0; i<sliders.length; i++) {
if (!sliders[i].getAttribute('data-slider-paused')) {
slide(sliders[i]);
}
}
}
function slide (slider) {
slider.sliderIndex++;
slider.children[0].style.marginLeft = -slider.clientWidth*(slider.sliderIndex%slider.children.length) + 'px';
}
function initSliders () {
for (var i=0; i<sliders.length; i++) {
var slider = sliders[i];
slider.sliderIndex = 1;
slider.onclick = clickSlider;
}
setInterval(slideAll, DELAY);
}
function clickSlider (e) {
if (!e.target.classList.contains(SLIDER_CLASS)) {
return;
}
var bounds = this.getBoundingClientRect();
if (e.clientX-bounds.left < bounds.width/2) {
this.sliderIndex+=sliders.length;
} slide(e.target);
}
/*SLIDER*/
#sliderbox {
margin-top:-10%;
min-height:100vh;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
}
.slider {
width:900px;
height:300px;
background-color:none;
display:flex;
overflow:hidden;
position:relative;
margin:0px 0;
border:1px solid #FF0
}
.slider:before,
.slider:after {
content:'\27A4';
position:absolute;
top:0;
bottom:0;
left:0;
font-size:400%;
display:flex;
align-items:center;
justify-content:center;
cursor:pointer;
color:#FF0;
padding:10px;
}
.slider:before {
transform:rotate(180deg);
}
.slider:after {
left:auto;
right:0;
}
.slider > div {
flex: 0 0 100%;
display:flex;
align-items:center;
justify-content:center;
font-size:200%;
color:#FFF;
transition:margin-left .3s;
}
<div id="sliderbox">
<div class="slider" data-slider-paused="true">
<div><img src="../aa7.jpg"/><img src="../aa2.jpg" style="margin-left:10%"/><img src="../aa5.jpg" style="margin-left:10%"/></li></div>
<div><img src="../aa1.jpg"/><img src="../aa3.jpg" style="margin-left:10%"/><img src="../aa8.jpg" style="margin-left:10%"/></div>
<div><img src="../aa6.jpg"/><img src="../aa9.jpg"style="margin-left:10%"/><img src="../aa12.jpg"style="margin-left:10%"/></div>
<div><img src="../aa4.jpg"/><img src="../aa11.jpg"style="margin-left:10%"/><img src="../aa13.jpg"style="margin-left:10%"/></div>
</div>
</div>

Changing background images with js

Im trying to work out script that will change background images every 3 sec using fadeIn, fadeOut, addClass and removeClass.
Is there a better way to do it using setInterval?
$("document").ready(function () {
$("#bg").delay(3000);
$("#bg").fadeOut(300);
$("#bg").removeClass('bg1');
$("#bg").addClass('bg2');
$("#bg").fadeIn(300);
$("#bg").delay(3000);
$("#bg").fadeOut(300);
$("#bg").removeClass('bg2');
$("#bg").addClass('bg1');
$("#bg").fadeIn(300);
});
btw. its not working properly.
HTML:
<div id="bg" class="ShowBG bg1"></div>
CSS:
#bg{
position:absolute;
width:100%;
height:70%;
background-size:cover;
background-position:center;
display:none;
}
.bg1{background-image:url("/img/index/bg1.png");}
.bg2{background-image:url("/img/index/bg2.png");}
Your method should work just fine but it's not the best way to write it: what if your graphic designer suddenly decides to add another background image in the cycle? Your code could become pretty long pretty fast. Here's how I would do it:
var backgroundClasses = ['bg1', 'bg2']; // Store all the background classes defined in your css in an array
var $element = $('.container'); // cache the element we're going to work with
var counter = 0; // this variable will keep increasing to alter classes
setInterval(function() { // an interval
counter++; // increase the counter
$element.fadeOut(500, function() { // fade out the element
$element.removeClass(backgroundClasses.join(' ')). // remove all the classes defined in the array
addClass(backgroundClasses[counter % backgroundClasses.length]). // add a class from the classes array
fadeIn(500); // show the element
});
}, 3000)
.container {
width: 100vw;
height: 100vh;
}
.bg1 {
background-color: red;
}
.bg2 {
background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container bg1"></div>
The hardest part of the code is this:
$element.addClass(backgroundClasses[counter % backgroundClasses.length])
It basically adds one of the classes stored in the backgroundClasses array. Using the modulo operator (%) on the counter will basically start over every time it has reached the end of the array, counting 0, 1, 0, 1, 0, 1 if you're array is only 2 elements long. If it's 3 elements long it counts 0, 1, 2, 0, 1, 2, ... and so on. Hope that makes sense.
Use callback of fadeOut() method (see complete parameter here) to perform class change when the animation is done. Otherwise the class will swap while the animation is still going.
There is no better way than using setInterval() if you want to do it automatically and continuously.
Here is working example:
$("document").ready(function () {
var bg = $("#bg");
setInterval(function() {
// We fadeOut() the image, and when animation completes we change the class and fadeIn() right after that.
bg.fadeOut(300, function() {
bg.toggleClass('bg1 bg2');
bg.fadeIn(300);
});
}, 1500);
});
#bg {
position:absolute;
width:100%;
height:70%;
background-size:cover;
background-position:center;
}
.bg1 {
background-image: url("https://www.w3schools.com/css/img_fjords.jpg");
}
.bg2 {
background-image: url("https://www.smashingmagazine.com/wp-content/uploads/2015/06/10-dithering-opt.jpg");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bg" class="ShowBG bg1"></div>
Edit
Just noticed OP wants fading so I added a simple CSS transition and opacity properties to both classes and #bg.
Use toggleClass(). Not sure why you used display:none so I removed it. Also I added the dimensions to html and body so your div has something to relate it's percentage lengths with.
Demo
setInterval(function() {
$('#bg').toggleClass('bg1 bg2');
}, 3000);
html,
body {
height: 100%;
width: 100%
}
#bg {
position: absolute;
width: 100%;
height: 70%;
background-size: cover;
background-position: center;
opacity:1;
transition:all 1s;
}
.bg1 {
background-image: url("http://placehold.it/500x250/00f/eee?text=BG1");
opacity:1;
transition:all 1s;
}
.bg2 {
background-image: url("http://placehold.it/500x250/f00/fff?text=BG2");
opacity:1;
transition:all 1s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="bg" class="ShowBG bg1"></div>

Jquery, Stopped queued functions and reset to original function

So I am trying to get this function to work. I have made other functions like this before I just like to make new things just for fun. As of now I am trying to make this image that flashes out and back in as a new image. so far it works fine but the only problem is that it gets overloaded and acts funny when clicked repeatedly. I tried putting it in JSfiddle but some of the animations appeared to not make it through. regardless I was wondering if there was a way to, if you click on something and start the function again while the original time it was clicked has not finished processing through. I could stop the function and reset it back to the beginning instead of watching the image flicker again and again until it works through the amount of times it was clicked.
HTML
<div id="menuImageDiv">
<div id="menuImageLeft"><</div>
<img id="menuImage" src="pictures/gingerbread.JPG">
<div id="menuImageRight">></div>
</div>
Jquery
$(function(){
var i = 0;
var cars = ['pictures/gingerbread.JPG', 'pictures/cupcake.JPG','pictures/cake.JPG'];
var delay = 500
function startSlide(){
interval = setInterval(function(){
var menuimage=$("#menuImage")
i ++;
i = i % cars.length;
menuimage.toggleClass('nopacity', delay).promise().done(function(){
menuimage.attr('src', cars[i]).promise().done(function(){
menuimage.toggleClass('nopacity', delay)
});
});
}, delay*10)
}
function stopSlide(){
clearInterval(interval);
};
$("document").ready(function(){
var menuimage=$("#menuImage");
var right = $("#menuImageRight");
right.click(function(){
menuimage.toggleClass('nopacity', delay).promise().done(function(){
menuimage.attr('src', cars[i]).promise().done(function(){
menuimage.toggleClass('nopacity', delay)
});
});
i ++;
i = i % cars.length;
console.log(i);
})
})
$("document").ready(function(){
var menuimage=$("#menuImage")
var left = $("#menuImageLeft")
left.click(function(){
menuimage.toggleClass('nopacity', delay).promise().done(function(){
menuimage.attr('src', cars[i]).promise().done(function(){
menuimage.toggleClass('nopacity', delay)
});
});
i --;
i = i % cars.length;
if (i<=-1){
i=cars.length-1;
}
})
})
startSlide();
$("#menuImageDiv").mouseenter(stopSlide).mouseleave(startSlide)
})
CSS
#menuImageDiv{
z-index:0;
position:relative;
height:500px;
width:100%;
margin: 0 auto;
top:-30px;
}
#menuImage{
width:100%;
min-height:500px;
max-height:550px;
}
#menuImageLeft{
cursor: url(http://i68.tinypic.com/2zq4h2g.jpg), auto;
margin:auto 0;
font-size:50px;
background:#3a3a3a;
padding:0 25px;
opacity:0;
top:50px;
position:absolute;
float:left;
z-index:5;
width:47%;
height:400px;
}
#menuImageRight{
cursor: url(http://i63.tinypic.com/2mni1ky.jpg), auto;
font-size:50px;
background:#3a3a3a;
padding:0 25px;
opacity:0;
top:50px;
float:right;
position:absolute;
z-index:5;
width:50%;
height:400px;
left:50%
}
.nopacity{
opacity:0;
}
https://jsfiddle.net/nathanahartmann/30d0ause/8/
P.S. I've been looking into .stopPropogation() and .stopImmediatePropogation() and .stop() and none of them have worked as desired.
Since you are using jQuery. Please look at these documentation.
jQuery Finish
jQuery Stop
Jquery UI toggleClass has a callback function for when the animation completes
menuimage.finish().toggleClass('nopacity', delay,function(){
menuimage.attr('src', cars[i]);
menuimage.toggleClass('nopacity', delay);
});
demo:https://jsfiddle.net/w4dnb7qd/

FadeIn and fadeOut DIV border for 3 seconds only

How do I keep a fadeIn and fadeOut active for 3 seconds and clear the effect?
html:
<div id="divid"></div>
css:
#divid{
border:1px solid;
border-color:#000;
}
jQ:
var Interval;
function fadeInOut(divid){
$('#'+divid).fadeOut(500).fadeIn(500);
};
Interval = setInterval(fadeInOut(divId),600);
How do I keep a fadeIn and fadeOut active for 3 seconds and clear the
effect?
If interpret Question correctly, try utilizing .queue() to call .fadeIn(500).fadeOut(500) three times sequentially , then stop; queue should be empty, or "clear the effect"
$("#divid").queue("_fx", $.map(Array(3), function() {
return function(next) {
return $(this).fadeOut(500).fadeIn(500, next);
}
})).dequeue("_fx");
#divid {
border:1px solid;
border-color:#000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div id="divid">abc</div>
There's a lot going on here. First off, I don't think you need an interval here. Also, the second param for fadeIn is a callback. You can pass the fadOut method there. Finally, change the iteration to 3000 milliseconds (3 seconds)
fadeInOut();
function fadeInOut() {
$('#divid').fadeOut(3000, function() {
$(this).fadeIn(3000);
});
}
#divid{
border:1px solid;
border-color:#000;
width: 100px;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="divid"></div>

I need to close a gap in this JQuery animation

Here is my Jquery code:
var img = function() {
$(".slider#1").delay(1000).fadeIn(1000);
$(".slider#1").delay(3000).fadeOut(1000);
$(".slider#2").delay(5000).fadeIn(2000);
$(".slider#2").delay(3000).fadeOut(1000);
$(".slider#3").delay(10000).fadeIn(2000);
$(".slider#3").delay(3000).fadeOut(1000);
$(".slider#4").delay(15000).fadeIn(2000);
$(".slider#4").delay(3000).fadeOut(1000, function() { img() });
};
Essentially what I am trying to do is when one image fades out I would like an image to almost be behind it and fade straight into that without being a blank space in between, is this possible?
You could use the jQuery fadeTo function.
Like
$(".slider#1").fadeTo(1000,1);
And make all your sliders overlap each other with opacity 0.
Edit :
You can try this fiddle :
http://jsfiddle.net/8cwA6/22/
It recursively changes the opacity. All the images are on top of each other, then it fades out Level 1, then Level 2, and then fades both of them back (because Level 3 is on the bottom). You'll probably understand better when you see the code.
JavaScript
var max = 3
var min = 1
var showTime = 1500
function fade(num) {
if (num > min) {
$('.' + num).delay(showTime).fadeTo("slow", 0, function() {
fade(num - 1);
});
} else {
$("div").delay(showTime).fadeTo("slow", 1, function() {
fade(max)
});
}
}
fade(3);
HTML
<div id="img1" class="1"></div>
<div id="img2" class="2"></div>
<div id="img3" class="3"></div>
CSS
#img1 {
width:100px;
height:100px;
background-color:red;
position:absolute;
top:0;
left:0;
opacity :1;
z-index :1;
}
#img2 {
width:100px;
height:100px;
background-color:blue;
position:absolute;
top:0;
left:0;
opacity :1;
z-index :2;
}
#img3 {
width:100px;
height:100px;
background-color:green;
position:absolute;
top:0;
left:0;
opacity :1;
z-index :3;
}
You have to queue the animations, or they will get mixed up after some time, since your animations are running asynchronous;
I did stack all images, and all are visible (you could set z-index just to be certain).
Fading out the top most, the next one is showing up.
The bottom most, doesn't have to be faded. I fade in the first one again, before resetting/showing all the other images once again and resetting the recursion.
jsfiddle
http://jsfiddle.net/Drea/hkzbvew4/
js
var images = ['.slider1', '.slider2', '.slider3', '.slider4']; // .slider4 is always visible
var img = function (i, showtime, fadetime) {
$(images[i]).delay(showtime).fadeOut(fadetime).queue(function () {
i++;
if (i < images.length-1) { // run through images
img(i, showtime, fadetime);
$.dequeue(this);
} else { // reset animation
$(images[0]).delay(showtime).fadeIn(fadetime).queue(function () {
$(".slide").fadeIn(0);
img(0, showtime, fadetime);
$.dequeue(this);
});
$.dequeue(this);
}
});
};
img(0, 1000, 1000);
html
<div class="slide slider4"></div>
<div class="slide slider3"></div>
<div class="slide slider2"></div>
<div class="slide slider1"></div>
css
.slide {
width: 50px;
height: 50px;
position: absolute;
top: 50px;
left: 50px;
}
.slider1 {
background-color: black;
}
.slider2 {
background-color: green;
}
.slider3 {
background-color: blue;
}
.slider4 {
background-color: red;
}

Categories

Resources