setInterval / setTimeout for revealing elements one by one - javascript

I have a simple for loop inside a rendering function.
for (let i = 0; i < PLAYER.hand.length; i++) {
playerHTML += `<img class='card' src='images/cards/${PLAYER.hand[i]}.svg'>`;
}
I am trying to implement a card draw animation. How can I implement that?
I tried for example:
for (var i = 0; i < PLAYER.hand.length; i++) {
(function(i) {
setInterval(function() {
playerHTML += `<img class='card' src='images/cards/${PLAYER.hand[i]}.svg'>`;
}, 5000)
})(i)}
did not work for me. any ideas?

If you are looping through the player hand like that, your code will create an interval for each of the element and add them to playerHTML every 5000ms.
You can achieve what you want throught the use of setInterval and manually tracking/increasing the current index like this:
let i = 0
let maxLen = 5 // amount of cards on player hand
let interval = 1000
const myInterval = setInterval(() => {
console.log(i) // add to player hand
i++ // go to next card
if (i >= maxLen) { // breaking out of interval condition
clearInterval(myInterval);
}
}, interval)

you are already using interval so probably dont need the for loop
(function() {
let i = 0;
const interval = setInterval(function() {
playerHTML += `<img class='card' src='images/cards/${PLAYER.hand[i]}.svg'>`;
i += 1;
if (i >= PLAYER.hand.length) {
clearInterval(interval);
}
}, 5000)
})()

Another approach could be to initially load all the images but not display them. Then, in a setInterval() loop the existing hidden class could be removed from every element, one by one:
const cont=document.querySelector(".content");
cont.innerHTML=[...Array(5)].map((_,i)=>
`<img src="https://picsum.photos/id/${237+i}/150/100" class="hidden">`).join("<br>");
const intv = setInterval(() => {
const img=cont.querySelector(".hidden");
if(img) img.classList.remove("hidden");
else clearInterval(intv);
},1000)
.hidden {display:none}
<div class="content"></div>

Related

How to make pause between each for loop iteration in JavaScript

I'm sorry if there has already been such question, I haven't found.
I wanna make a progress (loading) animation with JS:
<div class = "load">
<div id = "load_fill"></div>
<p id = "percent">0%</p>
</div>
<script>
let percent = document.getElementById("percent");
let load_fill = document.getElementById("load_fill")
let j = 0;
let fill = () => {
j++;
percent.textContent = `${j}%`;
load_fill.style.width = `${j}%`
}
for (let i = 0; i < 100; i++){
setTimeout(fill, 200);
}
</script>
The problem is that the first iteration works with delay but others no;
Is there any way to make delay between each iteration?
Will be thankful for any answer.
new Array(100).map((el, ind) => ind).forEach((cur) => setTimeout(fill, (cur + 1) * 200))
Here, we space out each setTimeout by first creating a range array (the first part) before setting a time out 200 ms between each one.

clearTimeout(timer) not taken into account - New timer variable created each time

In the current slideshow I'm building, I use a timer variable to rotate the slides automatically every 4s. Since manual controls are also present, I wanted to reset this timer whenever the controls are used to avoid any premature succession between two slides, but didn't manage to do it.
I supposed it to be a scope problem, but the timer variable is out of the functions that are trying to share it (showSlide(n) and changeSlide(n)). Yet a brand new timer variable seems to be created each time the changeSlide function is called : the slides automatic rotation quickens each time the "next" control is used, as if multiple timeouts were calling the function simultaneously. What is wrong here ?
const slideshows = document.getElementsByClassName("js-slideshow");
[].forEach.call(slideshows, function(slideshow) {
slideshowlize(slideshow);
});
function slideshowlize(slideshow){
const desc = slideshow.getElementsByClassName("js-desc");
const slide = slideshow.getElementsByClassName("js-slide");
let timer;
let index = 0;
const slidePrev = slideshow.querySelector('.js-prev');
const slideNext = slideshow.querySelector('.js-next');
function showSlide(n){
clearTimeout(timer); // This one is not used yet
if(n < 0){
n = slide.length -1;
}
else if(n > slide.length -1){
n = 0;
}
let i;
for(i = 0; i < slide.length; i++){
slide[i].classList.remove("is-shown");
}
for(i = 0; i < desc.length; i++){
desc[i].classList.remove("is-shown");
}
slide[n].classList.add("is-shown");
desc[n].classList.add("is-shown");
index = n;
timer = setTimeout(function(){
changeSlide(1);
}, 4000);
}
function changeSlide(n){ // this is where the magic doesn't happen
clearTimeout(timer);
if (n > 0){
showSlide(index += 1);
} else {
showSlide(index -= 1);
}
timer = setTimeout(function(){
changeSlide(1);
}, 4000);
}
showSlide(index);
slidePrev.addEventListener('click', function(){
changeSlide(-1);
});
slideNext.addEventListener('click', function(){
changeSlide(1);
});
}
Edit : Two different timers were set up. Since showSlide(n) were already resetting the timer, changeSlide(n) had no need to do it too. Thanks to Bergi for pointing it out.
function changeSlide(n){
//removed "clearTimeout(timer);"
if (n > 0){
showSlide(index += 1);
} else {
showSlide(index -= 1);
}
//removed "timer = setTimeout(...);"
}

Adding 2 Delays In Javascript Loop

I am building a javascript function that displays a popup 10 times each time for 5 seconds.
Inside my code I have something like this:
for (i=0; step < 10; i++)
{
showPopup();
//need to add 5 second delay here
hidePopup();
//need to add a 5 second delay again
}
I have tried settimeout funcion but am not able to syncronize the delays.
I would appreciate anyone helping me to complete this.
You can utilize await inside your loop to wait for a Promise that resolve after 5 seconds to create delays.
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function main() {
for (let i = 0; i < 10; i++) {
showPopup();
await delay(5000);
hidePopup();
await delay(5000);
}
}
You can use setTimeout and to synchronise the delays use the iteration's index, here is an example:
for (i=0; i < 10; i++) {
setTimeout(showPopup, (i * 2) * 500);
setTimeout(hidePopup, ((i * 2) + 1) * 500);
}
function showPopup() {
console.log("#popup show");
}
function hidePopup() {
console.log("#popup hide");
}
In this example, I've set the delay to 500 milliseconds instead of 5 seconds so you won't have to wait too long to see the effect.
You could do with setInterval for repeat the loop ever 5 second and use settimeout for display time of the data
var interval = '';
var count = 1; //count for check 10 times
function show() {
if (count <= 10) {
interval = setInterval(() => {
count++;
el.style.display = 'block'
clearInterval(interval)
setTimeout(hide, 5000)
}, 5000);
}
}
var el = document.querySelector('p');
function hide() {
el.style.display = 'none';
show()
}
show()
p {
display: none
}
<p>hello</p>
Use SetInterval instead of loop and then stop the setInterval using clearInterval

Issue with triggering a function after has been run initially

I have a function called pageReload which sets the a timer and variables back on that page to start, when the time is counting down, however when the timer reaches 0 it seems to disable the function even though when the function is called again the time should be set back to 18 as specified in the function.
When it's between 18 and 0 it trigger ok and sets the time back to 18, the other parts seems to work ok (number of tries and matches set back)
I've tried different variations without getting it to work so below if the function together with the other code in the app which might give a bit of context to what I'm doing
"use strict";
//select each card
const cards = document.querySelectorAll('.card');
let isFlipped = false;
let setBoard = false;
let first, second;
let counter = 1;
//add event listeners to each square
for(let i = 0; i < cards.length; i++) {
let element = cards[i];
element.addEventListener('click', flipSquare);
}
function checkForMatch() {
//check for 2 matching squares
let isMatch = first.classList.value === second.classList.value;
$('#counter').html(`The number of tries made is: ${counter++}`);
isMatch ? disable() : unflip();
//check to see if completed - if so, score will be displayed
completed();
}
function checkScore(){
//determing whether a score A, B or unsuccessful were acheived
if(counter <= 15) {
$('#score').html("You got an A");
}
else if(counter > 15 && counter <= 20){
$('#score').html("You got an B");
} else {
$('#score').html("You had too many attempts and were therefore unsuccessful");
}
}
function completed(){
//pop up if all have been disabled
if($('.card:not(.open)').length === 0){
//display modal
$("#myModal").modal('show');
clearInterval(timerId);
clearTimeout(myTimeout);
elemComplete.html(timeComplete + ' seconds comleted in');
}
//check score on completion and output the result
checkScore();
}
let timeLeft = 18;
let timeComplete;
let elem = $('#some_div');
let elemComplete = $('#new_div');
let timerId = setInterval(showClock, 1000);
function shuffleCards() {
//give square random positions
for(let i = 0; i < cards.length; i++) {
let ramdomPos = Math.ceil(Math.random() * 12);
cards[i].style.order = ramdomPos;
}
}
function pageReload(){
shuffleCards();
//loop through any open cards to and remove their open status and add back click function to unflipped card
for(let i = 0; i < cards.length; i++) {
$(".card").removeClass('open');
let element = cards[i];
element.addEventListener('click', flipSquare);
}
isFlipped = false;
setBoard = false;
timeLeft = 18;
counter = 0;
n = 0;
$('#counter').html(`The number of tries made is: ${counter}`);
$('#updated').html(`The number of matches made is: ${n}`);
counter++;
}
I'm not 100% sure as I don't think this is all of the code, but I have a feeling that you are stopping your timer in the completed() function using clearInterval() and never restarting it?
Presuming this is the cause, I would try resetting the timer in your page reload function.
function pageReload(){
shuffleCards();
//loop through any open cards to and remove their open status and add back click function to unflipped card
for(let i = 0; i < cards.length; i++) {
$(".card").removeClass('open');
let element = cards[i];
element.addEventListener('click', flipSquare);
}
isFlipped = false;
setBoard = false;
timeLeft = 18;
counter = 0;
n = 0;
timerId = setInterval(showClock, 1000);
$('#counter').html(`The number of tries made is: ${counter}`);
$('#updated').html(`The number of matches made is: ${n}`);
counter++;
}
This makes the timer code a little fragile, so you could refactor the timer logic out into its own functions and do something like this to make things a little clearer:
let timerId = undefined;
function startTimer() {
if (timerId != undefined) {
stopTimer();
}
timerId = setInterval(showClock, 1000);
}
function stopTimer() {
clearInterval(timerId);
timerId = undefined;
}
You would then remove all of you existing timer code and call startTimer() in pageReloaded() and stopTimer() in completed()

How to run a javascript function X seconds?

I am using setInterval to run a Javascript function that generates a new, random integer in a div. the timer starts when I click on the div. I am having problems with stopping it form generating new numbers after five seconds.
Using setTimeout, I hide the div after 5 seconds; that stops random numbers, but I lose the div.
How can I efficiently stop the generating of numbers in the div, and not hide it?
HTML:
<div id="div" onmousedown='F();'>Click here</div>
JS:
function F(){
var div = document.getElementById("div");
setInterval(function(){
var number = Math.floor(Math.random()*28) ;
div.innerHTML = number;
}, 1000);
setTimeout(function(){
div.style.display = 'none';
},5000);
};
Just use a counter to keep track of the number of times the interval has ticked and then use clearInterval to stop it:
var count = 0;
var intervalID = setInterval(function() {
// generate your random number
count++;
if (count === 5) {
clearInterval(intervalID);
}
}, 1000);
Something hastily written, but what you want to do is keep track of your interval handle and then clear it. You can do this with a setTimeout
var forXsecs = function(period, func) {
var handle = setInterval(func, 1000);
setTimeout(function() { clearInterval(handle); }, period * 1000);
}
The timing is not perfect. Matt's answer would also work.
Another option is a slight change on Matt's answer that removes setInterval and just uses timeouts.
var count = 0;
var forXsecs = function(period, func) {
if(count < period) {
func();
count++;
setTimeout(function() {forXsecs(period, func);}, 1000);
} else {
count = 0; //need to reset the count for possible future calls
}
}
If you just want to simply let it run once each second and that 5 times you can do it like this:
HTML:
<div id="5seconds"></div>
JS:
var count= 0;
setInterval(function(){
if(count < 5){
document.getElementById('5seconds').innerHTML = Math.random();
count++
}
},1000);
This will generate a random number each second. until 5 seconds have passed
you should use clearInterval to stop the timer.
To do so, you pass in the id(or handle) of a timer returned from the setInterval function (which creates it).
I recommend clearing the interval timer (using clearInterval) from within the function being executed.
var elm = document.querySelector("div.container");
var cnt = 0;
var timerID;
function generateNumber()
{
cnt += 1;
elm.innerText = cnt;
if (cnt >= 5) {
window.clearInterval(timerID);
}
}
timerID = window.setInterval(generateNumber, 1000);
.container {display:block; min-width:5em;line-height:5em;min-height:5em;background-color:whitesmoke;border:0.1em outset whitesmoke;}
<label>1s Interval over 5s</label>
<div class="container"></div>

Categories