How do I delay an execution in Javascript? - javascript

i swear i've being looking for the way to solve this many hours before I ask here, I found some codes but still nothing fits to what I need... the thing is that I have some boxes and 1 button for rolling a random style... when I click the button the following code starts;
function roll(){
for (i = 0; i < 4; i++) {
setInterval(function(){
res = Math.floor(Math.random() * 10) + 1;
/*document.getElementById("slot-" + res).style.border = "5px solid red";
document.getElementById("slot-" + res).style.color = "red";*/
document.getElementById("slot-" + res).className = "col-sm-2 slot-active";
}, 500);
document.getElementById("slot-" + res).className = "col-sm-2 slot";
}
};
It selects at random between 1 and 10 and changes style class to that box... so... in the line after the loop, i pretend to put original stile back so it gives a look of a random selection... but it doesnt executes, if I put it inside the for loop it executes inmediatly and seems like nothing is happening... im Javascript noob and i'm trying this to learn and practice!... thanks in advance for your help!...

You can create a setInterval that will randomly set the selected class, and have a setTimeout to end the execution of said interval, something like this: https://jsfiddle.net/canastro/xxdbjm4n/1/
const refreshIntervalId = setInterval(() => {
$('.selected').removeClass('selected');
const res = Math.floor(Math.random() * 10) + 1;
$(`.slot-${res}`).addClass('selected');
}, 500)
setTimeout(() => {
clearInterval(refreshIntervalId);
}, 3000);

You could use setTimeout(function(){dosomething}, timeout), btw. add console.log('something') to see if the functions are actually executed.
btw. if you're using interval remember you might need to cancel it if you dont want it running forver, you could as well use recurring function (a function calling itself) with some condition on when to do processing or now.
btw2. every time you create interval assign it to some variable or something so you can cancel it!

your answer should be like this.you need to see javascript variable scope.
function roll(){
for (i = 0; i < 4; i++) {
var res = Math.floor(Math.random() * 10) + 1;
function active(res){
setTimeout(function(){
document.getElementById("slot-" + res).className = "col-sm-2 slot-active";
}, 500);
document.getElementById("slot-" + res).className = "col-sm-2 slot";
}
active(res);
}
};
div{
width:50px;
height:50px;
background:#ccc;
float:left;
margin:5px;
}
.slot{
background:#0f0;
}
.slot-active{
background:#f00;
}
<button onclick="roll()" style="display:block;">roll</button>
<div id="slot-1"></div>
<div id="slot-2"></div>
<div id="slot-3"></div>
<div id="slot-4"></div>
<div id="slot-5"></div>
<div id="slot-6"></div>
<div id="slot-7"></div>
<div id="slot-8"></div>
<div id="slot-9"></div>
<div id="slot-10"></div>

Related

Loop with delay inside other loop

I'm trying to transform this loop that changes the value of a varible number of inputs:
$("#wrap input").each(function( index ) {
for( i = 10 ; i > 0 ; i--) {
$(this).val(i);
console.log( index + ": " + $(this).val() );
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrap">
<input type="text">
<input type="text">
<input type="text">
</div>
I need to have a small delay between each iteration. I'm trying some variatons of this:
$("#wrap input").each(function( index ) {
var a = $(this);
var i = 10;
var loop = setInterval(function() {
a.val(i);
console.log( index + ": " + a.val() );
i--;
if(i==0) clearInterval( loop );
},1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrap">
<input type="text">
<input type="text">
<input type="text">
</div>
It doesn't work like I need.
I need to change the value in order, first input changes to 10 (delay) changes to 9 (delay)... second input changes to 10 (delay) changes to 9 (delay)... and so on. Hope you get the idea.
I'm thinking to assign them different ids via jQuery and then make a loop through each one separately, but I need a script as brief as possible.
Any changes to the structure have to be done with js.
The value update has to be in order
There must be a delay in each iteration
The code has to be as simple as possible
I'm a little stuck, I appreciate any hint.
Final update:
#acontell gave a perfect answer for the example I was dealing with in my question. Yet I realized why I didn't use next() since the begginnig: in the real project the inputs are not immediate siblings, they are each inside a couple of containers. Anyway the loop proposed by #acontell suits fine, I just had to put the elements in an array, like this:
var list = [];
$("#wrap input").each(function(index) {
list.push($(this));
});
var i = 10; // Countdown start
var n = 0; // Array index
var loop = setInterval(function() {
if (n > list.length - 1) {
clearInterval(loop);
} else {
list[n].val(i--);
if (i < 0) {
n++;
i = 10;
}
}
}, 100);
<div id="wrap">
<div><div><div><input type="text"></div></div></div>
<div><div><div><input type="text"></div></div></div>
<div><div><div><input type="text"></div></div></div>
<div><div><div><input type="text"></div></div></div>
<div><div><div><input type="text"></div></div></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I'm not really sure of what you're after, but if you want to wait for each counter (in each input) to finish and then start the next counter, a linked list comes to mind.
Jquery provides the method next that gets you the next sibling. That could help you to build the linked list. The iteration over the list could be done using an interval that would be cleared when there were no more siblings. Putting it all together:
var $element = $("#wrap input:first"),
countdownLimit = 10;
var loop = setInterval(function() {
if (!$element.length) {
clearInterval(loop);
} else {
$element.val(countdownLimit--);
if (countdownLimit < 0) {
$element = $element.next('input');
countdownLimit = 10;
}
}
}, 1000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrap">
<input type="text">
<input type="text">
<input type="text">
</div>
There can be as many inputs as you like as long as they all are siblings.
Any time you need to have a delay in iterations, it makes sense to use a self-calling function instead of a loop.
function doStuff(data, i){
//do things here
console.log(i)
if(--i) setTimeout(function(){doStuff(data, i)}, 1000)
else nextStep(data)
}
function nextStep(data){
//after the loop ends, move to the next step of your code
}
doStuff([], 10)
Have you tried the equivalent of thread sleep?
var millisecondsToWait = 500;
setTimeout(function() {
// Whatever you want to do after the wait
}, millisecondsToWait);

setTimeout, JavaScript, no effect

I need to animate something, but my statement has no effect. My code:
var movei=function(img){
img.setAttribute("src", "blank.png");
}
var comp=function() {
...
for(var k=0; k<i; k++) {
var img=document.getElementById(id(k,col));
img.setAttribute("src", "circ1.png");
timer=setTimeout(movei(img),1000);
...
}
}
I read another questions about setTimeout (setInterval) but I couldn`t find an answer.
try timer=setTimeout(function(){movei(img);},1000);
If you want to animate individual elements, you should use recursion instead of loops.
Following code depicts the same:
var count = 0;
function getId(count) {
return "div_" + count;
}
function initTimer() {
setTimeout(function() {
count++;
var _id = getId(count);
var _el = document.getElementById(_id);
show(_el);
if (count < 7)
initTimer();
}, 1000);
}
function show(el) {
el.style.display = "block";
}
initTimer();
div {
display: none;
}
<div id="div_1">1</div>
<div id="div_2">2</div>
<div id="div_3">3</div>
<div id="div_4">4</div>
<div id="div_5">5</div>
<div id="div_6">6</div>
As #deceze has also written, you need to change the call to setTimeout so that you do not call the movei function, but rather pass the function 'name'. Then you would of cause have to change the behavior the function movei so it either gets a simple string name of the image or can lookup the image itself.
timer=setTimeout(movei,1000);
or (in the not recommented way)
timer=setTimeout("movei(\"imgName\")",1000);

Why won't my HTML Images fade

I'm trying to create a simple slideshow effect. I have 10 images, and I've created a basic HTML page with 2 buttons to go to the right or left image. On clicking the button, the images change.
Now, I'm trying to add a basic fade functionality to the changing image. But the fade effect isn't getting displayed. When I put alerts, I notice that the fade is taking place, but without the alerts it is too fast to be visible. Also, it is happening on the previous image, instead of the next one.
<html>
<head>
<style>
.main {
text-align: center;
}
.centered {
display: inline-block;
}
#image {
border: solid 2px;
width: 500px;
height: 500px;
}
#number {
font-size: 30px;
}
</style>
<script>
function goLeft() {
var image = document.getElementById("image");
var pos = document.getElementById("number");
if(Number(pos.innerHTML)==1) {
image.src = "Images\\10.jpg"
pos.innerHTML = 10;
} else {
image.src = "Images\\" + (Number(pos.innerHTML)-1).toString() + ".jpg"
pos.innerHTML = (Number(pos.innerHTML)-1).toString();
}
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
}
function changeOpacity(image, i) {
alert(parseFloat(i*4/100).toString());
image.style.opacity = (parseFloat(i*4/100).toString()).toString();
}
function goRight() {
var image = document.getElementById("image");
var pos = document.getElementById("number");
if(Number(pos.innerHTML)==10) {
image.src = "Images\\1.jpg"
pos.innerHTML = 1;
} else {
image.src = "Images\\" + (Number(pos.innerHTML)+1).toString() + ".jpg"
pos.innerHTML = (Number(pos.innerHTML)+1).toString();
}
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
}
</script>
</head>
<body>
<div class="main">
<div class="centered">
<img id="image" src="Images\1.jpg">
</div>
</div>
<div class="main">
<div class="centered">
<span id="number">1</span>
</div>
</div>
<div class="main">
<div class="centered">
<button onclick="goLeft()" style="margin-right:50px;">Go Left</button>
<button onclick="goRight()" style="margin-left:50px;">Go Right</button>
</div>
</div>
</body>
The problem is this block of code that is in your goLeft method, and goRight method:
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
You are creating 25 timers that, and each timer will execute approximately 1 second later.
Creating animations is best left to the CSS.
In your CSS add:
#image {
transition: opacity 0.5s ease;
}
And then in your JavaScript, simply: image.style.opacity = 1.0;
When the opacity changes, CSS will automatically transition the opacity length at the speed defined in the css, e.g 0.5s. Feel free to experiment.
I also added a jsfiddle here: http://jsfiddle.net/dya7L8wq/
You misunderstood setTimeout and the for loop.
Norman's answer provides a good solution with CSS, but he doesn't talk too much about why your code is not working. So I'd like to explain.
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
You assumption is:
invoke changeOpacity(image, 0) after 1 second
invoke changeOpacity(image, 1) 1 second after step 1
invoke changeOpacity(image, 2) 1 second after step 2
invoke changeOpacity(image, 3) 1 second after step 3
....
And the last step is invoking changeOpacity(image, 24) 1 second after previous step.
What actually happens is:
The loop is finished almost immediately!
In each iteration, setTimeout queues an asynchronous function invocation, and it's done! That says, it will return right away, rather than wait until changeOpacity returns.
And then, after about 1 second, changeOpacity fires 25 times almost at the same time, because you queued it 25 times in the for loop.
Another problem here is: in changeOpacity invocations, passed-in parameter i are not 1, 2, 3...., they all have the same value that causes for loop to exit (1 second ago) - 25, because JS doesn't have a block scope prior to ES6 (in ES6 we have keyword let for it).
In a pure JS solution, to ensure the time sequence we'd usually queue next invocation at the end of every step:
function changeOpacity() {
// do something here
// before the function returns, set up a future invocation
setTimeout(changeOpacity, 1000)
}
Here's an example to print a list of numbers from 1 to 5:
var go = document.getElementById('go')
var op = document.getElementById('output')
var i = 0
function printNum() {
var p = document.createElement('p')
p.innerHTML = ++i
op.appendChild(p)
// next step
if(i < 5) {
setTimeout(printNum, 500)
}
}
go.onclick = printNum
<button id="go">GO</button>
<div id="output"></div>
Why use pure JavaScript?
Use jQuery.
It has a pretty neat fadeTo() function and a useful fadeIn() function.
Might wanna use that ;)

jQuery fade in box with unique content

I am making an info screen, and for that, it needs to show reviews from their customers pulled from Trustpilot.
I got the reviews and everything formatted in HTML showing the 20 latest, but I want to present it very sweet. I am not a JavaScript guru, but I thought i would do it using jQuery and its fadein function.
What is want, is have 20 unique divs fading in with X milliseconds difference popping randomly up. By unique I mean, that each div must have unique content. And by randomly popping up, I mean that if box 1 spawns first, then the next should be 5, then 14 etc, and then another cycle the next time around.
Just like what I made here;
$(function() {
var box = $('.box');
var delay = 100;
for (i = 0; i < 30; i++) {
setTimeout(function() {
var new_box = box.clone();
$('.container').append(new_box);
new_box.fadeIn();
}, delay);
delay += 500; // Delay the next box by an extra 500ms
}
});
http://jsfiddle.net/CCawh/5/
Is this even possible, and how would this be done?
I am very new to JavaScript, so please bear with me if I ask to much
Thanks in advance.
EDIT:
The HTML i want to spawn will all be wrapped in divs, so it would go like this;
<div id="one">content</div>
<div id="two">content</div>
<div id="three">content</div>
<div id="four">content</div>
etc.
Made up a nice function for you. I believe this may be what you are looking for
Here's a rundown of how it works :
Populate an array with numbers randomly generated 1-10 in this case.
Run through that array with a set interval, and when everything has
been added stop the interval
pretty straightforward from there. Set the visibility etc. You should be able to change up the function to dynamically add HTML elements and what-not, but just giving you something to start with.
var usedNum = [];
var i, j, y;
i = 0;
for(y = 0; y < 10; y++){
var x = Math.floor((Math.random() * 10) + 1);
if(!isUsed(x)) usedNum.push(x);
else y--;
}
var showInterval = setInterval ( function(){
if(i == 10){
clearInterval(showInterval);
}
$(".container div[data-line='" + usedNum[i] + "']").css({opacity: 0.0, visibility: "visible"}).animate({opacity: 1.0});
i++;
}, 500);
function isUsed(num) {
var used = false;
for(j = 0; j < usedNum.length; j++){
if(usedNum[j] == num){
used = true;
}
}
return used;
}
Demo fiddle : http://jsfiddle.net/xS39F/3/
Edit:
You can also mess around with the speed of the animation. In this demo (http://jsfiddle.net/adjit/XYU34/1/) I set the speed to 1000 so the next element starts fading in before the last element was done fading in. Makes it look a little smoother.
Instead of using a for loop and setTimeout, would setInterval work better for what you need? Some HTML might help better understand what you're trying to achieve.
$(function() {
var box = $('.box');
var delay = 100;
var interval = setInterval(function() {
var new_box = box.clone();
$('.container').append(new_box);
new_box.fadeIn();
}, delay);
delay += 500; // Delay the next box by an extra 500ms
}, delay);
});

jQuery slider - last to first transition

I created this slider (didn't want to use plugins):
function slider(sel, intr, i) {
var _slider = this;
this.ind = i;
this.selector = sel;
this.slide = [];
this.slide_active = 0;
this.amount;
this.selector.children().each(function (i) {
_slider.slide[i] = $(this);
$(this).hide();
})
this.run();
}
slider.prototype.run = function () {
var _s = this;
this.slide[this.slide_active].show();
setTimeout(function () {
_s.slide[_s.slide_active].hide()
_s.slide_active++;
_s.run();
}, interval);
}
var slides = [];
var interval = 1000
$('.slider').each(function (i) {
slides[i] = new slider($(this), interval, i);
})
The problem I have is that I don´t know how to get it after the last slide(image), it goes back to the first slide again. Right now, it just .hide and .show till the end and if there is no image it just doesn´t start again.
Can someone help me out with a code suggestion to make it take the .length of the slider(the number of images on it) and if it is the last slide(image), then goes back to the first slide(image)... like a cycle.
Edit: Slider markup
<div class="small_box top_right slider">
<img class="fittobox" src="img/home10.jpg" alt="home10" width="854" height="592">
<img class="fittobox" src="img/home3.jpg" alt="home3" width="435" height="392">
<img class="fittobox" src="img/home4.jpg" alt="home4" width="435" height="392">
</div>
Created a fixed version for you here.
The easiest way to do this is to run a simple maths operation where you currently have
_s.slide_active++;
Instead, I get _s.slide_active, add 1, then run that through modulus (%) to the total length — which gives the remainder:
_s.slide_active = (_s.slide_active + 1) % _s.slide.length;
Take a look at this Fiddle link, this will help you create the slider in a cyclic way.If the slider reaches the last image it will start again from the first image.
var index = $selector.index();
if (index == (length - 1)) {
$('img').first().removeClass('invisible').addClass('visible');
}
I hope this will help you more. All the best.
You need to get to 0 after length-1.
One simple way to do that is to work modulo length:
_s.slide_active++;
_s.slide_active %= length;
not tested but hope helpful :
function slider(sel, intr , i){
...
this.count = this.selector.children().length;
this.run();
}
slider.prototype.run = function(){
var _s = this;
this.slide[this.slide_active].show();
setTimeout(function(){
_s.slide[_s.slide_active].hide()
if(_s.slide_active == this.count)
_s.slide_active = 0;
else
_s.slide_active++;
_s.run();
}, interval);
}

Categories