Why won't this loop? - javascript

I have written a javascript program that changes the picture on a header, waits, then shows the next one.
I have tried to make it loop round back to 1 when it reaches the last picture, but it's not working, the computer keeps crashing and I'm really stuck.
Later on it's going to have options to fade and have different transitions, but right now, I can't even get the function to loop forever without crashing the computer.
Can anyone offer a solution?
Thank you,
Andrew
var $initialDelay = 0;
var $delay = 200;
var $picture = 1;
var $pictureAmount = 4;
function myFunction() {
//first picture loaded in CSS
//loop through pictures
for (i=0;i<$pictureAmount;i++) {
setTimeout(function(){
document.getElementById("header").style.backgroundImage = "url(img/slideshow/slideshow"+$picture+".jpg)";
$picture++;
}, $initialDelay);
$initialDelay += $delay;
}
}
myFunction();

If you want this to continuously change picture every 200ms
var delay = 200;
var picture = 1;
var pictureAmount = 4;
function myFunction() {
setTimeout(function() {
document.getElementById("header").style.backgroundImage = "url(img/slideshow/slideshow" + picture + ".jpg)";
picture = (picture % pictureAmount) + 1;
myFunction();
}, delay);
}
myFunction();

Here is an example for looping over a 4 images with a timeout. I think you should be able to use this in your code to do what you want.
const
delayBetweenPictures = 1000,
numberOfPictures = 4,
initialPicture = 1,
image = document.getElementById('slideshow');
function changeToPicture(pictureIndex) {
console.log(`Changing picture to index ${pictureIndex}`);
// Change the image
image.src = `http://lorempixel.com/320/200/cats/${pictureIndex}`;
// Use a modulo operator to turn 4 (number of pictures) back to 0 and add 1 so the range becomes 1...number of pictures.
pictureIndex = (pictureIndex % numberOfPictures) + 1;
// Set a timeout of X ms after which the changeToPicture method is called again, this time with the new value of pictureIndex.
setTimeout((newIndex) => changeToPicture(newIndex), delayBetweenPictures, [pictureIndex]);
}
changeToPicture(initialPicture);
<img id="slideshow">

By using the modulo operation you can loop between the values 1 to 4 over and over.
It would be easier if your pictures were "0-indexed" :
for (i=0; /* infinite loop */; i=(i+1)%$pictureAmount) {
// loops through 0, 1, ..., $pictureAmount - 1
}
but can be adjusted for a 1-indexed iteration :
for (i=1; /* infinite loop */; i=(i%$pictureAmount)+1) {
// loops through 1, 2, ..., $pictureAmount
}
Example iteration over 1 to 9 :
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function loop() {
for (i=1; /* infinite loop */; i=(i%9)+1) {
document.getElementById("view").innerHTML=i;
await sleep(1000);
}
}
loop();
<p id="view"></p>

Related

Set correct timer for slideshow

I'm new to this and can't find a way to do it.
I have 4 images but can't set a rule that moves changes correctly that slideshow image.
Example: 1 - 2 - 3 - 4, if I go from 1 and click to go to 3, timer makes me go to 2 because it was in 1.
onload = start;
function start(){
var i = 1;
function Move(){
i = (i%4)+1;
document.getElementById('i'+i).checked = true;
}
setInterval(Move,10000);
}
You need to keep the current element index out of the start function and change it when you select an element manually:
onload = start;
var currIndex = 1;
function start() {
function Move(){
currIndex = (currIndex % 4) + 1;
document.getElementById('i' + currIndex).checked = true;
}
setInterval(Move,10000);
}
// better add a class here for simpler selector
document.querySelectorAll('#i1, #i2, #i3, #i4').addEventListener('whatever-event-you-get-on-selection', function() {
currIndex = yourSelectedId; // use data attributes or something for easier index extraction
});

How to loop a function in javascript

I have four different button effects where each effect are declared in a variable.
Therefore, I bring all of these four variables and place them within an array called arr in which is used in the clickByItself() function using Math.floor(Math.random()) methods.
Without the for loop, the code clicks by itself randomly in one of the four buttons every time I reload the page.
function clickByItself() {
let random = Math.floor(Math.random() * arr.length);
$(arr[random]).click();
}
However, using the for loop I am not being able to make these clicks one-by-one within the maximum of 10 times.
var blueButtonEffect = code effect here;
var redButtonEffect = code effect here;
var greenButtonEffect = code effect here;
var yellowButtonEffect = code effect here;
var arr = [blueButtonEffect, redButtonEffect, greenButtonEffect, yellowButtonEffect];
//will click on buttons randomly
function clickByItself() {
let random = Math.floor(Math.random() * arr.length)
var i;
for (i = 0; i < 10; i++) {
$(arr[random]).click();
setTimeout(clickByItself(), 1000);
}
}
The final output with the current code above is the four buttons being clicked at the same time, not one-by-one.
So, how can I have this function to press a random button by 10 times one-by-one with one second of interval from each click?
To fix your code you need:
A base case for your recursion
Pass a function reference to setTimeout. Currently, you are executing clickByItself and passing its return value (which is undefined) to setTimeout.
Do not use setTimeout in a loop without increasing the time by a factor of i, as the for loop will queue all the function calls at the same time
Alternatively, you can use a "times" argument to avoid looping
You could try something like
function clickByItself(times = 0) {
let random = Math.floor(Math.random() * arr.length)
$(arr[random]).click();
if (++times < 10) {
setTimeout(function(){clickByItself(times);}, 1000);
}
}
An example with console logs
https://jsfiddle.net/pfsrLwh3/
The problem is that the for loop calls the setTimeout 10 times very quickly. If you want to wait until the previous function call finishes prior to calling the next, then you should use recursion or just use a setInterval.
Recursion:
function clickByItself(numIterations) {
let random = Math.floor(Math.random() * arr.length)
let i;
$(arr[random]).click();
if( numIterations < 10 ){
setTimeout(() => {
clickByItself(numIterations += 1)
}, 1000)
}
}
clickByItself(0);
With setInterval
let numIterations = 0;
function clickByItself() {
let random = Math.floor(Math.random() * arr.length);
let i;
$(arr[random]).click();
numIterations += 1;
if( numIterations > 10) {
clearInterval(loop)
}
}
let loop = setInterval(test2, 1000);
Are you saying this is working for only 4 times but I think your above code will run in an infinite loop as you are calling clickByItself() again in the for loop.
If you want press a random button by 10 times one-by-one with one second of interval from each click then replace the for loop with
for (i = 0; i < 10; i++) {
setTimeout($(arr[random]).click(), 1000);
}

Javascript Loop through Array while displaying the elements

I want to create a 'random selector' behaviour where the function iterates through an array for a period of time (example: 3 seconds, 5 seconds) while displaying all of array elements fast throughout the iteration until the iteration ends. Just imagine seeing all the elements displayed in a label one after another until it finally stops at an element.
My code so far:
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 100; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
setTimeout(function () {
place_label.innerText = selected_place;
}, 400);
}
}
This runs through the iteration and displays an element when the loop is done but it doesn't show each iteration's element. How can I amend this?
EDIT
Even if there's 3 elements, the animations must re-iterate through the array until the duration is completed
Your for finishes iterating before the setTimeout runs, then the function passed to setTimeout runs 100 times using the last value of selected_place.
More about this behavior in setTimeout in for-loop does not print consecutive values
Another problem that I noticed is that your setTimeout will trigger after 400ms, since the for loop will finish in about 1 ms, the function passed to setTimeout will trigger 100 times one after another with no delay in between, for this, I replaced the delay argument of setTimeout from 400 to 400 * i.
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 100; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
(function(selected_place){
setTimeout(function () {
place_label.innerText = selected_place;
}, 400 * i);
})(selected_place);
}
}
execute_randomizer();
<span id="place_label"></span>
You could use a closure over the value and a different time for each timeout.
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 10; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
setTimeout(function (value) {
return function () {
place_label.innerText = value;
};
}(selected_place), i * 100);
}
}
execute_randomizer();
<div id="place_label"></div>
For a first run through, you could show each element and then take a random element at last value.
function execute_randomizer() {
function cb (value) {
return function () {
place_label.innerText = value;
};
}
var place_label = document.getElementById("place_label");
place_label.innerText = '';
for (var i = 0; i < places.length; i++) {
setTimeout(cb(places[i]), 200 + i * 200);
}
setTimeout(cb(places[Math.floor(Math.random() * places.length)]), 200 + places.length * 200);
}
var places = ["Curry Leaf", "Subway", "Burger King"];
execute_randomizer();
<div id="place_label"></div>
You should change your loop because right now you go in loop 100 times in maybe one millisecond and order 100 of change text but once again in the same time.
So better is wait for time out 400 ms and then make next iteration.
Please remember time out is async.
I think it might be better if you would put the whole function in a timeout. (I'm not sure and didn't test, but that's what I would try).
What I mean is that you just build a function that creates your randomizer and fills in your field. After that you put a timeout to call the same function again + keep a counter that you pass as parameter.
An example of what I mean below: (didn't test it)
var places = ["Curry Leaf", "Subway", "Burger King"];
execute_randomizer(0); /* Start for the first time */
function execute_randomizer(times) {
if(times == 100)
return; /* stop execution */
var place_label = document.getElementById("place_label");
var selected_place = places[Math.floor(Math.random() * places.length)];
place_label.innerText = selected_place;
setTimeout(function () {
execute_randomizer(times+1)
}, 400);
}

Canvas issues with updating

<canvas id="ctx" width="500" height="500" style="border:1px solid #000000;">
<script type="text/javascript">
window.onload = function() {
var ctx = document.getElementById("ctx").getContext("2d");
function createText(words) {
var LENGTH = words.length;
var LOOPS = LENGTH;
var reader = 0;
position = 10;
while( LOOPS > 0) {
letter = words.substr(reader,1);
setTimeout(ctx.fillText(letter,position,10),100);
position += 6;
reader += 1;
LOOPS -= 1;
}
}
createText("Hello, how are you?");
}
</script>
</canvas>
I want it to do kind of like a typing animation where it paused for a fraction of a second before each letter is printed, but instead it loads all at the same time. What am I doing wrong?
So there were a few things making this not work for you, for setTimeout your ctx.fillText was being called right away, as soon as the loop hit it. To stop that you need to wrap it in a function so it will be called during the timeout.
setTimeout(function(){
// your logic here.
}, 100);
However if you do that you will run into the common issue where you will only get the last letter due to the way variable scoping works in JavaScript. To fix that you need to wrap your function in a closure and pass the values to it.
// loop start
a++;
b++;
setTimeout(
(function (a,b) {
return function () {
// some logic that uses a and b
}
})(a, b), 100);
// loop end
The last thing that happens is your timeout is set to 100.. so it will all still happen at once. Meaning every timeout is going to fire after 100ms since the creation loop is so fast. In order to solve this you need to save the delay somewhere and increase that in the loop so they happen after one another. For example the first one will be delayed 100ms, and the next one 200ms, then 300, ect.
// loop start
a++;
b++;
// Increase the time delay each loop iteration
timeDelay += 100;
setTimeout(
(function (a,b) {
return function () {
// some logic that uses a and b
}
})(a, b), timeDelay);
// loop end
Full working code and demo
Live Demo
window.onload = function () {
var ctx = document.getElementById("ctx").getContext("2d");
function createText(words) {
var LENGTH = words.length;
var LOOPS = LENGTH;
var reader = 0;
var timeDelay = 100;
position = 10;
while (LOOPS > 0) {
letter = words.substr(reader, 1);
setTimeout((function (letter, position) {
return function () {
ctx.fillText(letter, position, 10);
}
})(letter, position), timeDelay);
position += 6;
reader += 1;
LOOPS -= 1;
timeDelay += 100;
}
}
createText("Hello, how are you?");
}

changing images automatically with javascript

I have the current JavaScript problem. I have four divisions next to each other on my website that constantly rotate images on a 10 seconds interval. I need these intervals to keep rotating images at the current interval but start 5 seconds apart from each other in order to obtain a nice waterfall effect. How can I accomplish this using JavaScript?
image of how it looks on my websites' header
This is an example of the code I am currently using to display a single division and handle the rotation of the images.
<div class = "TestRotator">
<img src="http://bushveld.info/wp-content/uploads/2013/07/image1.png" alt="rotating" width="100" height="232" id="rotator">
<script type="text/javascript">
(function () {
var rotator = document.getElementById('rotator'); // change to match image ID
var imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/';
var delayInSeconds = 5;
// set number of seconds delay
// list image names
var images = ['image2.png', 'image3.png', 'image4.png'];
var num = 0;
var changeImage = function () {
var len = images.length;
rotator.src = imageDir + images[num++];
if (num == len) {
num = 0;
}
};
setInterval(changeImage, delayInSeconds * 1000);
})();
</script>
</div>
I've fiddled it a lot! (I changed it big time.)
chenged setInterval() with setTimeout() and many others.
Is this what you wanted?
PS: state holds the 1st image to which the imgs change. and the difference in the timeout (200 milliseconds is in order to just to make some difference in between them, yuo can change it to a round number if you want to).
If I've understood your question correctly, you need something like this:
window.onload = function () {
var // Customable parameters
imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/',
interval = 2, // Interval between "flushes" in seconds. Must be > speed * maxScreens
speed = 0.1, // "Flush" speed in seconds
maxScreens = 4, // amount of used image tags
images = 4, // amount of image sources, can be > maxScreens
dir = 1, // 1 = from left to right, -1 = from right to left
// Program
flush,
target = (dir > 0) ? 1 : maxScreens,
targetOrigo = target,
changeImage = function() {
var img = document.getElementById('rotator' + target),
id = parseInt(img.src.substr(img.src.length - 5, 1), 10) - dir;
if (id < 1) {
id = images;
}
if (id > images) {
id = 1;
}
img.src = imageDir + 'image' + id + '.png';
if (target !== maxScreens - targetOrigo + 1) {
target += dir;
setTimeout(changeImage, speed * 1000);
} else {
target = targetOrigo;
setTimeout(runRotation, interval * 1000);
}
return;
},
runRotation = function () {
setTimeout(changeImage, speed * 1000);
};
setTimeout(runRotation, 1000);
}
A live demo at jsFiddle
Notice, that I've put the function at window.onload, looks better when all the images are already loaded, before the rotation starts.
The snippet doesn't use setInterval() at all, instead it's based on nested setTimeout()s. This way you can avoid a mess, which you might get (depends on used browser), if user visits at other tab and then comes back to your site.
You can play with interval, "flush" speed, number of images you have on the rotation and even how many different images you like to use (max. = 9). You can also switch the direction of the rotation.
If you want to avoid two similar images to be shown at the same time, you can add image5.png to your image folder, and set images = 5.
Also version using an image source array available.
Thanx alot for the input. I solved this issue by adapting the code in this manner...
(function() {
var rotator3 = document.getElementById('rotator3'); // change to match image ID
var imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/';
// set number of seconds delay
// list image names
var images = ['image2.png', 'image3.png', 'image4.png', 'image1.png'];
// don't change below this line
var num = 0;
var changeImage = function()
{
var len = images.length;
rotator3.src = imageDir + images[num++];
if (num == len)
{
num = 0;
}
};
function SwImg() {
var rotate = setInterval(changeImage, 20000);
}
setTimeout(SwImg,15000);
})();
This tweak basically creates an initial delay of 5++ seconds at each division with the standard 20 seconds delay interval between switches, rotating each image in each division 5 seconds after the other. Here is a link to the website , will be done end of this week. Thanks again for the input, really awesome and creative ways of solving this issue!
Cheers

Categories