Using setTimeout func. & random func. to animate class elements - javascript

My debugging skills are not helping me much with figuring out what I am doing wrong here.
I want each element in an array to animate after a specified time using setTimeout function.
I am not getting any errors and the loop appears to run just fine, however, none of the elements in the array end up moving from their original place to the new spot.
function publicity()
{
// placing elements with class name 'cCameras' inside an array
var eCamerasArray = $(".cCameras").toArray();
// creating 2 arrays to hold left & top values of each element
var iLeftPosArray = [];
var iTopPosArray = [];
// loop to run through each element in array
for( var i = 0; i < eCamerasArray.length; i++)
{
// timer variable set for each element to be used in setTimeout func.
var timer = Math.floor (Math.random()*300) + 100;
// setTimeout func. used to animate each element after a specified (timer) time
window.setTimeout (function ()
{
iLeftPosArray[i] = Math.floor (Math.random() *139) + 360;
iTopPosArray[i] = Math.floor (Math.random() *160) + 100 ;
$(eCamerasArray[i]).animate ({left: iLeftPosArray[i] + "px", top: iTopPosArray[i] + "px"}, 100, "linear");
return [iLeftPosArray[i], iTopPosArray[i]];
}, timer);
}
}

You can fix it with creating closure:
(function publicity() {
var eCamerasArray = $(".cCameras"),
iLeftPosArray = [],
iTopPosArray = [],
timer;
for(var i = 0; i < eCamerasArray.length; i += 1) {
timer = Math.floor (Math.random() * 300) + 100;
(function (i) {
window.setTimeout (function () {
iLeftPosArray[i] = Math.floor (Math.random() * 139) + 360;
iTopPosArray[i] = Math.floor (Math.random() * 160) + 100 ;
$(eCamerasArray[i]).animate ({left: iLeftPosArray[i] + "px", top: iTopPosArray[i] + "px"}, 300, "linear");
return [iLeftPosArray[i], iTopPosArray[i]];
}, timer);
}(i));
}
}());
You can see the effect here: http://jsfiddle.net/zHUAt/2/
Best regards!

Unrolling a simple loop you can see what happens:
var i = 0;
window.setTimeout( function(){
//No local i so it must be outside
console.log(i);
}, 1000 );
i++;
window.setTimeout( function(){
//No local i so it must be outside
console.log(i);
}, 1000 );
i++;
window.setTimeout( function(){
//No local i so it must be outside
console.log(i);
}, 1000 );
As you can see, all the functions refer to the same i, so
they will all log 2 once the timers fire. None of them
have a local i.
You can create a "local" i like this:
(function(i){
|---------^ //i found here, no need to use the global i
| window.setTimeout( function(){
-------------------- //no local i here so it must be outside
console.log(i);
}, 1000 );
})(i) //pass the "global" i as argument, with the value it has right now

Related

How to run setTimeout() with a random interval in each iteration in javascript?

I'm trying to iterate over a string array with a delay of a few milliseconds every step of iteration. Something like below -
var l = ['a', 'b', 'c'];
var delay = 5000;
for(var i = 0;i < l.lenght;i++) {
document.querySelectorAll("a[title='" + l[i] + "']")[0].parentNode.children[0].click();
delay = 5000 + Math.floor(Math.random() * 5000) + 1;
**<WAIT for 'delay' number of milliseconds**
}
I've been able to convert the code to below using setTimeout() method -
var i = 0;
var interval = setInterval(function() {
if (i < l.length) {
document.querySelectorAll("a[title='" + l[i] + "']")[0].parentNode.children[0].click();
i++;
}
else {
clearInterval(interval);
}
//delay = 5000 + Math.floor(Math.random() * 5000) + 1); **NOT SURE where to change the delay variable**
}, delay);
But the delay variable essentially becomes a constant once setTimeout kicks off. How to change the delay variable in each iteration?
You can make a recursive timer function for this:
Try the following :
function displayValue(){
let arr = ['a', 'b', 'c'];
let delay = 1000;
let i = 0;
function timerFunction(i){
if(i === arr.length)
return;
setTimeout(()=>{
console.log(arr[i]);
i++;
timerFunction(i);
}, delay);
delay = delay + 1000 + Math.floor(Math.random() * 4000);
}
timerFunction(i);
}
displayValue();
Could you try replacing that delay variable with a function such as this?
var i = 0;
var interval = setInterval(
function() {
if (i < l.length) {
document.querySelectorAll("a[title='" + l[i] + "']")[0].parentNode.children[0].click();
i++;
}
else {
clearInterval(interval);
}
},
function() {
return 5000 + Math.floor(Math.random() * 5000) + 1;
}
);
setInterval, which you have used in your example, initializes a callback launching every (circa) N ms, where N is fixed. You have to clear it later on with clearInterval.
setTimeout on the other hand means - invoke my callback after ~N time. You could then call another setTimeout inside the callback, with a different N.
as an example:
function callback() {
/*
your logic here
*/
delay = 5000 + Math.floor(Math.random() * 5000) + 1);
setTimeout(callback, delay); // for your "clearInterval" case - just don't invoke this
}
setTimeout(callback, initialDelay);
This thread appears the closest I found to the fundamental question of how to iterate through an array at a random time interval, which isn't as easy as one would think.
Although arguably a little general for the specific question asked, the currently accepted answer looks the closest match to the fundamental question. The code below is a minor modification which looks to provide a random delay on the first pass and logs each random delay after the logging each array value.
function displayValue(){
let arr = ['a', 'b', 'c','d', 'e'];
let delay = Math.floor(Math.random()*10000);
let i = 0;
function timerFunction(i){
if(i === arr.length)
return;
setTimeout(()=>{
console.log(arr[i]);
console.log(`delay was ${Math.floor(delay/1000)} seconds`);
i++;
timerFunction(i);
}, delay);
delay = Math.floor(Math.random()*10000);
}
timerFunction(i);
}
displayValue();
The value of the above modification probably depends on the context.
Interestingly, applying the introduction to forEach from bootcamp provides an alternate solution which is probably closer related to the OP!
var colors = ["red", "green", "blue", "yellow"];
function printArr(x){
var random = Math.round(Math.random()*60000);
setTimeout(() => {
console.log(x);
console.log(`Delay was ${Math.floor(random/1000)} seconds`)
}, random);
};
colors.forEach(printArr);

Delay javascript code execution inside for loop

I have the following for loop:
for (var i = tileLog.length - 1; i >= 0; i--) {
$('.' + tileLog[i]).mouseenter();
};
1 < tileLog.legth < 1025
Is there a way to delay each iteration of the loop so that mouseenter() is triggered every x miliseconds?
I have tried:
function doSetTimeout(i) {
setTimeout(function() { $('.' + i).mouseenter(); }, 250);
}
for (var i = tileLog.length - 1; i >= 0; i--)
doSetTimeout(tileLog[i]);
This doesn't seem to work, it just delays by 250ms then iterates through the loop
As an alternative to using setTimeout() you could also use setInterval().
Define a running variable in the outer scope (like your running i in the loop).
In each iteration, besides calling your function, decrement the running variable. If it is below zero, stop the setInterval()`` :
var index = tileLog.length - 1,
timer = setInterval( function(){
$('.' + tileLog[index]).mouseenter();
index -= 1;
if ( index < 0 ) {
clearInterval( timer );
}
}, 250 );
There is no actual sleep() function or something similar. Would also be problematic as JavaScript (for most cases) is single threaded and such a method would block the render thread, thus rendering your browser inaccessible.
There is no sleep or such in JavaScript. So your approach with timeout is correct.
var tileLog;
var i = titleLog.length - 1;
function func1() {
$('.' + tileLog[i]).mouseenter();
if (--i) {
window.setTimeout(func1, 250);
}
}
// and of course start the process
func1();

Javascript setTimeout not working in a for-loop

I have to change the source of an image every second. I have a for loop in which a call a function that has a timeout. I read that here, on stackOverflow, but it doesn't work. Can please someone tell me what can I fix to make it work? I've been struggling with this for much more that I'd like to admit. Thanks.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script type="text/javascript">
function changeImage(k) {
setTimeout(function(){
document.getElementById("img").src = k + ".png"; alert(k );}, 1000);
}
function test() {
for (var k = 1; k <= 3; k++) {
changeImage(k);
}
}
</script>
</head>
<body>
<div id="main_img">
<img id="img" src="http://placehold.it/110x110">
</div>
<input type="button" style="width: 200px" onclick="test()" />
</body>
In your code, you set all the timeouts at once. So if you set them all one second from now they all fire one second from now.
You are already passing in the index k so just multiply the time parameter by k.
setTimeout(function(){
document.getElementById("img").src = k + ".png";
alert(k);
}, k * 1000);
// ^ added
The problem is that you are creating instances of a timer milliseconds apart. One second later, they all execute milliseconds apart as well. What you need is to execute them at a set interval apart from each other.
You can use a timer using setInterval, which executes the provided function at a given interval. Don't forget to kill-off the timer though, otherwise it will run forever.
Minor optimizations
You can cache the element in a variable so you won't be hitting the DOM that frequently.
Also, I'd avoid the alert(). If you are debugging, use breakpoints in the debugger. If you really want it to be "alert-like", then use console.log and watch the console.
An advantage of setInterval over a recursive setTimeout is that you will not be spawning multiple timers per iteration, but instead, just one timer.
And here's the proposed solution:
var k = 0;
var image = document.getElementById("img");
var interval = setInterval(function() {
// Increment or clear when finished. Otherwise, you'll leave the timer running.
if(k++ < 3) clearInterval(interval);
image.src = k + ".png";
// Execute block every 1000ms (1 second)
},1000);
Instead of using loop, you can do it like this:
var k = 0;
var int = setInterval(function() {
if (k <= 3) k++;
else { clearInterval(int); }
document.getElementById("img").src = k + ".png";
alert(k);
}, 1000);
My advice is to use console.log() or alert() to help you debug - it'll make it a LOT more obvious what's going on. For instance, if you put a console.log in your test or setTimeout functions, you'd see that all three images were getting added at the same time.
What I'd recommend is to declare your "nextImage" function, then define your setTimeout within that function. That way it'll call itself every second.
Another tip: I assume you want the three images to loop forever, so I added an often used trick with the modulus operator (%) to accomplish this.
Have a look:
Working demo: http://jsfiddle.net/franksvalli/PL63J/2/
(function(){
var numImages = 3, // total count of images
curImage = 1, // start with image 1
$image = document.getElementById("img"),
imageBase = "http://placehold.it/110x11";
function nextImage() {
$image.src = imageBase + curImage;
// increment by one, but loop back to 1 if the count exceeds numImages
curImage = (curImage % numImages) + 1;
// execute nextImage again in roughly 1 second
window.setTimeout(nextImage, 1000);
}
// initializer. Hook this into a click event if you need to
nextImage();
})();
As other folks have said, you probably want to use setInterval, which you can do with some tweaks:
(function(){
var numImages = 3, // total count of images
curImage = 1, // start with image 1
$image = document.getElementById("img"),
imageBase = "http://placehold.it/110x11";
function nextImage() {
$image.src = imageBase + curImage;
// increment by one, but loop back to 1 if the count exceeds numImages
curImage = (curImage % numImages) + 1;
}
// initializer. Hook this into a click event if you need to
nextImage(); // call function immediately without delay
window.setInterval(nextImage, 1000);
})();
The problem
setTimeout doesn't stop the program execution but only sets up an event for a callback in 1 second. What that means is that if you setup three setTimeout's inside your for loop, they will execute simultaneously after 1 second.
A solution
Instead of using a for loop, you can use a delayed recursion.
function changeImage(imageIndex) {
document.getElementById("img").src = imageIndex + ".png";
alert(imageIndex);
}
function myLoop( imageIndex ) {
if( imageIndex >= 3 ) return;
changeImage( imageIndex );
setTimeut( function() { myLoop(imageIndex + 1) }, 1000 );
}
setTimeut( function() { myLoop(0) }, 1000 );
Another solution using setInterval
var interval = null;
var imageIndex = 0;
function changeImage() {
document.getElementById("img").src = imageIndex + ".png";
alert(imageIndex);
imageIndex++;
if( imageIndex === 3 ) clearInterval( interval );
}
interval = setInterval( changeImage , 1000);
Using different delays
function changeImage(imageIndex) {
document.getElementById("img").src = imageIndex + ".png";
alert(imageIndex);
}
for( var i=0; i < 3; i++) {
setTimeout( changeImage.bind(window, i), i * 1000 );
}
A groovy one liner( please don't use this, ever! )
(function f(i) { setTimeout( changeImage(i) || f.bind(window, i = (i++)%3), 1000); })(0)
WHY IT DOESN'T WORK?
Because Javascript always passes variables by reference. When your code is waiting on the queue, the variables have already changed.
MY SOLUTION:
Create an array and push whatever codes you want to execute in order of appearance (Place the real value of the variables directly) e.g.:
var launcher = [];
launcher.push('alert("First line of code with variable '+ x +'")');
launcher.push('alert("Second line of code with variable '+ y +'")');
launcher.push('alert("Third line of code with variable '+ z +'")');
Use setInterval instead of setTimeout to execute the codes (You can even change the delay period dynamically) e.g.
var loop = launcher.length;
var i = 0;
var i1 = setInterval(function(){
eval(launcher[count]);
count++;
if(i >= loop) {
clearInterval(i1);
}
}, 20);

jQuery - setInterval issue

I am using jQuery to generate and add a random amount of Clouds to the Header of the page and move them left on the specified interval. Everything is working fine, execpt the interval only runs once for each Cloud and not again. Here is my code:
if(enableClouds) {
var cloudCount = Math.floor(Math.random() * 11); // Random Number between 1 & 10
for(cnt = 0; cnt < cloudCount; cnt++) {
var cloudNumber = Math.floor(Math.random() * 4);
var headerHeight = $('header').height() / 2;
var cloudLeft = Math.floor(Math.random() * docWidth);
var cloudTop = 0;
var thisHeight = 0;
var cloudType = "one";
if(cloudNumber == 2) {
cloudType = "two";
}else if(cloudNumber == 3) {
cloudType = "three";
}
$('header').append('<div id="cloud' + cnt + '" class="cloud ' + cloudType + '"></div>');
thisHeight = $('#cloud' + cnt).height();
headerHeight -= thisHeight;
cloudTop = Math.floor(Math.random() * headerHeight);
$('#cloud' + cnt).css({
'left' : cloudLeft,
'top' : cloudTop
});
setInterval(moveCloud(cnt), 100);
}
function moveCloud(cloud) {
var thisLeft = $('#cloud' + cloud).css('left');
alert(thisLeft);
}
}
Any help is appreciated!
This is the way to go:
setInterval((function(i){
return function(){
moveCloud(i);
};
})(cnt), 100);
Engineer gave you the code you need. Here's what's happening.
The setInterval function takes a Function object and an interval. A Function object is simply an object that you can call, like so:
/* Create it */
var func = function() { /* ... blah ... */};
/* Call it */
var returnVal = func(parameters)
The object here is func. If you call it, what you get back is the return value.
So, in your code:
setInterval(moveCloud(cnt), 100);
you're feeding setInterval the return value of the call moveCloud(cnt), instead of the the function object moveCloud. So that bit is broken.
An incorrect implementation would be:
for(cnt = 0; cnt < cloudCount; cnt++) {
/* ... other stuff ... */
var interval = setInterval(function() {
moveCloud(cnt);
}, 100);
}
Now, you're feeding it a function object, which is correct. When this function object is called, it's going to call moveCloud. The problem here is the cnt.
What you create here is a closure. You capture a reference to the variable cnt. When the function object that you passed to setInterval is called, it sees the reference to cnt and tries to resolve it. When it does this, it gets to the variable that you iterated over, looks at its value and discovers that it is equal to cloudCount. Problem is, does not map on to a Cloud that you created (you have clouds 0 to (cloudCount -1)), so at best, nothing happens, at worst, you get an error.
The right way to go is:
setInterval((function(i){
return function(){
moveCloud(i);
};
})(cnt), 100);
This uses an 'immediate function' that returns a function. You create a function:
function(i){
return function(){
moveCloud(i);
};
}
that returns another function (let's call it outer) which, when called with a value i, calls moveCloud with that value.
Then, we immediately call outer with our value cnt. What this gives us is a function which, when called, calls moveCloud with whatever the value of cnt is at this point in time. This is exactly what we want!
And that's why we do it that way.

Increment integer by 1; every 1 second

My aim is to create identify a piece of code that increments a number by 1, every 1 second:
We shall call our base number indexVariable, I then want to: indexVariable = indexVariable + 1 every 1 second; until my indexVariable has reached 360 - then I wish it to reset to 1 and carry out the loop again.
How would this be possible in Javascript? - if it makes a difference I am using the Raphael framework.
I have carried out research of JavaScript timing events and the Raphael delay function - but these do not seem to be the answer - can anyone assist?
You can use setInterval() for that reason.
var i = 1;
var interval = setInterval( increment, 1000);
function increment(){
i = i % 360 + 1;
}
edit: the code for your your followup-question:
var interval = setInterval( rotate, 1000);
function rotate(){
percentArrow.rotate(1,150,150);
}
I'm not entirely sure, how your rotate works, but you may have to store the degrees in a var and increment those var too like in the example above.
var indexVariable = 0;
setInterval(function () {
indexVariable = ++indexVariable % 360 + 1; // SET { 1-360 }
}, 1000);
Try:
var indexVariable = 0;
setInterval(
function () {
indexVariable = (indexVariable + 1) % 361;
}, 1000}

Categories

Resources