Countdown starts without a click event - javascript

Question from a JS learner.
I'm making a countdown from 10 to 0 for this game like project. However, the countdown starts without a click event. I want it to start as an event listener but I don't know how to stop this function from running.
<div class="display-buttons-container">
<div class="display">10</div>
<button id="arm">Arm</button>
<button id="disarm">Disarm</button>
</div>
const btnArm = document.querySelector("#arm");
const btnDisarm = document.querySelector("#disarm");
const countdown = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
const countdownDisplay = document.querySelector(".display");
let i = 0;
btnArm.addEventListener("click", countdownFunction());
function countdownFunction() {
countdownDisplay.innerHTML = countdown[i];
++i;
if (i > 10) countdownDisplay.innerHTML = 0;
}
setInterval(countdownFunction, 1000);

Here the setInterval is not inside the countdownFunction function thats the reason it running without any click event.
and to stop the timer you can use clearInterval(intervalID) which is used to clear a timer that is set with the setInterval() method.
const btnArm = document.querySelector("#arm");
const btnDisarm = document.querySelector("#disarm");
// no need of this array you can use a single variable instead
// const countdown = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
let count = 10;
let intervalID;
const countdownDisplay = document.querySelector(".display");
function startTimer() {
count = 10;
// clearing timer in case user wants to restart the timer
if (intervalID) {
clearInterval(intervalID); // clearing timer
intervalID = undefined; // resetting intervalID
}
// intervalID will be used to clear the timer
intervalID = setInterval(() => {
if (count === 0 && intervalID) {
clearInterval(intervalID); // clearing timer
intervalID = undefined; // resetting intervalID
}
countdownDisplay.innerHTML = count;
--count;
}, 1000);
}
function stopTimer() {
// clearing timer if intervalID is defined i.e. if any timer is in running state.
if (intervalID) {
clearInterval(intervalID); // clearing timer
intervalID = undefined; // resetting intervalID
}
}
<div class="display-buttons-container">
<div class="display">10</div>
<button id="arm" onclick="startTimer()">Arm</button>
<button id="disarm" onclick="stopTimer()">Disarm</button>
</div>

You need to remove the parentheses after countdownFunction to stop it from running the function immediately
You need to save the interval id returned from setInterval
you can call clearInterval and pass the interval id as the parameter and it will stop the interval
const btnArm = document.querySelector("#arm");
const btnDisarm = document.querySelector("#disarm");
const countdown = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
const countdownDisplay = document.querySelector(".display");
let i = 0;
let intervalId
function countdownFunction() {
countdownDisplay.innerHTML = countdown[i];
++i;
if (i > 10) countdownDisplay.innerHTML = 0;
}
btnArm.addEventListener("click", () => {
if (intervalId) return
intervalId = setInterval(countdownFunction, 1000)
});
btnDisarm.addEventListener("click", () => clearInterval(intervalId))
<div class="display-buttons-container">
<div class="display">10</div>
<button id="arm">Arm</button>
<button id="disarm">Disarm</button>
</div>

I generally prefer setTimeout for this kind of operation. That said the key to this is to declare a variable that can hold the timer that both the arm/disarm functions can use to either set the timer or clear the timer respectively.
btnArm.addEventListener("click", countdownFunction()); assigns the returned value of calling the function to the listener. Unless you're returning a function you don't want to do this. btnArm.addEventListener("click", countdownFunction); assigns a reference to the function instead.
I've made all the elements identifiable by class for consistency.
Added new function that clears the timer.
Removed the requirement for an array - you can just set a default for the current count.
Used textContent rather than innerHTML.
// Cache the elements
const arm = document.querySelector('.arm');
const disarm = document.querySelector('.disarm');
const display = document.querySelector('.display');
// Set up the listeners
arm.addEventListener('click', start);
disarm.addEventListener('click', stop);
// Declare the timer variable
let timer;
function start() {
// When `start` is called it immediately calls
// `loop` with default count of 10
function loop(count = 10) {
// If count > 0 display the count, and call loop
// again with a reduced count
if (count > 0) {
display.textContent = count;
timer = setTimeout(loop, 1000, --count);
// Otherwise "Boom!"
} else {
display.textContent = '💥 Boom! 💥';
}
}
// Call the loop for the first time
loop();
}
// If "disarm" is called clear the timer
function stop() {
clearTimeout(timer);
display.textContent = 'Disarmed';
}
button { font-size: 1.2rem; padding: 0.4em 0.8em; border: 1px solid #dfdfdf; border-radius: 5px; margin: 0.5em 0.25em; }
button:hover { cursor: pointer; border: 1px solid #454545; }
.arm { background-color: salmon; }
.disarm { background-color: lightgreen; }
.display { margin: 1em 0.25em; font-size: 1.6rem; }
<button class="arm">Arm</button>
<button class="disarm">Disarm</button>
<div class="display"></div>

Related

How to run a function when scroll reaches a certain component?

There are some components stacked on top of each other and the last component has a timer. I want the timer to start only when that component is visible on screen or when scroll is reached to that component. [REPL]
let count_val = 80;
let count = 0;
function startTimer() {
let count_interval = setInterval(() => {
count += 1;
if(count >= count_val) {
clearInterval(count_interval);
}
}, 100);
}
// check if scroll reached to component and run below function.
startTimer();
How do I achieve this?
Like commented this can be achieved using Intersection Observer and an action
REPL
<script>
let count_val = 80;
let count = 0;
function timer(node) {
let interval
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
interval = setInterval(() => {
count += 1;
if(count === count_val) {
clearInterval(interval);
}
}, 100);
} else {
clearInterval(interval)
count = 0
}
})
})
observer.observe(node)
return {
destroy: () => observer.disconnect()
}
}
</script>
<div use:timer>
<p>
Counter - {count}
</p>
</div>
<style>
div {
height: 100vh;
display: grid;
place-items: center;
background-color: teal;
color: #0a0a0a;
font-size: 4rem;
}
</style>

How to prevent setInterval from being called multiple times simultaneously?

Is there a way to make the setInterval work only once at a time?
My code should work this way:
You manually input the number of the seconds;
The "div" works as a timer and runs till reaching 0.
If you click "Click me" once, it will work as expected.
However, if you click the button multiple times fast enough, the even listener will be working simultaneously as many times as you clicked.
let button = document.getElementById('button');
let input = document.getElementById('input');
let timer = document.getElementById('timer');
function timerRunner () {
let startTime = input.value;
if (startTime <= 0) {
console.log('The number is <=0')
} else {
do {
timer.textContent = startTime;
let counter = startTime;
let interval = setInterval(()=> {
console.log(counter);
counter--;
timer.textContent = counter;
if (counter === 0) {
clearInterval(interval);
}
}, 1000);
} while (typeof (timer.textContent) == "number");
}
}
button.addEventListener('click', timerRunner)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.centeralizer {
display: block;
width: 200px;
margin: 20vh auto;
}
#input {
display: block;
margin-bottom: 35px;
width: 150px;
}
#button {
display: inline-block;
margin-right: 35%;
}
#timer {
display: inline-block;
}
</style>
<script src="index.js" defer></script>
</head>
<body>
<div class="centeralizer">
<input id="input" placeholder="Input the number" type="number">
<button id="button">Click me</button>
<div id="timer">0</div>
</div>
</body>
</html>
You should store the running interval in the upper scope and clear it if the button is pressed again.
const button = document.getElementById('button');
const input = document.getElementById('input');
const timer = document.getElementById('timer');
let interval = null;
function startTimer() {
const startTime = +input.value;
if (startTime <= 0) {
console.log('The number is <= 0');
return;
}
let counter = startTime;
timer.textContent = startTime;
if (interval) clearInterval(interval);
interval = setInterval(() => {
timer.textContent = --counter;
console.log(counter);
if (counter === 0) clearInterval(interval);
}, 1000);
}
button.addEventListener('click', startTimer)
You can add a boolean variable isRunning that tracks whether the timer is running. You set it on when the timer is running and set it off when the timer is done. Check if it's on when the button is clicked, and return if it is. You can prevent multiple instances of the timer being triggered.
let button = document.getElementById('button');
let input = document.getElementById('input');
let timer = document.getElementById('timer');
var isRunning = false;
function timerRunner () {
if(isRunning) {
return; //return if timer is already running
}
isRunning = true; //specify that the timer is running
let startTime = input.value;
if (startTime <= 0) {
console.log('The number is <=0')
} else {
do {
timer.textContent = startTime;
let counter = startTime;
let interval = setInterval(()=> {
console.log(counter);
counter--;
timer.textContent = counter;
if (counter === 0) {
isRunning = false; //specify that the timer is not running and can be run again
clearInterval(interval);
}
}, 1000);
} while (typeof (timer.textContent) == "number");
}
}
button.addEventListener('click', timerRunner)

How to implement a counter that counts in a given time duration JS?

I want to implement a counter that counts in the given time duration. If I want to count 100 in one second, then the gap between each count should be 10ms as a result by the time it gets counted 100 it becomes 1 second. For 1000 the gap between each count should be 1ms.
I've tried using setInterval and changing the delay of the counter dynamically based on the number I want to count, but there's a problem with setInterval, it is not working as expected. Other than using setInterval how can I implement this counter in JavaScript. Thank you in advance.
const btn = document.querySelector('button');
const counter1 = document.querySelector('.counter1');
const counter2 = document.querySelector('.counter2');
btn.addEventListener('click', ()=> {
increment(100, counter1);
increment(1000, counter2);
})
const increment = (n, counter) => {
let m = 0;
let time = 1000 / n;
setInterval(() => {
if (m < n) {
m += 1;
counter.textContent = m;
} else clearInterval(this);
}, time);
};
.counter-container {
display: flex;
}
.counter {
margin: 10px;
text-align: center;
}
button {
margin-left: 80px;
};
<div class="counter-container">
<div class="counter">
<h3>Counter1</h3>
<p class="counter1">100</p>
</div>
<div class="counter">
<h3>Counter2</h3>
<p class="counter2">1000</p>
</div>
</div>
<button>start</button>
setInterval() and setTimeout() are not actually reliable when it comes to timing. I would use a combination of those with javascript's Date.now(), which returns the number of milliseconds since the browser's/os's epoch. So you could do something like this:w
const btn = document.querySelector('button');
const counter1 = document.querySelector('.counter1');
const counter2 = document.querySelector('.counter2');
btn.addEventListener('click', ()=> {
increment(100, counter1);
increment(1000, counter2);
})
const increment = (n, counter, length=1000) => {
let start = Date.now()
let end = start + length
let interval = length / n;
setInterval(() => {
time = Date.now()
if (time < end) {
count = (time - start) / interval
counter.textContent = count;
} else {
counter.textContent = n;
clearInterval(this)
};
}, interval/2);
};
.counter-container {
display: flex;
}
.counter {
margin: 10px;
text-align: center;
}
button {
margin-left: 80px;
};
<div class="counter-container">
<div class="counter">
<h3>Counter1</h3>
<p class="counter1">100</p>
</div>
<div class="counter">
<h3>Counter2</h3>
<p class="counter2">1000</p>
</div>
</div>
<button>start</button>

How to stop setInterval and resume?

How can I stop setInterval and then resume from the same place?
Example:
start ---> 1,2,3,4,5 .... ---> stop ---> start ---> 6,7,8 ...
index.html
<div onclick=start()>start</div>
<div onclick=stop()>stop</div>
index.js
let refreshInterval = null;
start() {
var i = 0;
refreshInterval = setInterval(function() {
console.log(i);
i++;
}, 1000);
}
stop() {
???????
}
You need a global variable i and for stopping a clearInterval.
function start() {
if (refreshInterval !== undefined) return; // prevent more than one interval
refreshInterval = setInterval(function() {
console.log(i);
i++;
}, 1000);
}
function stop() {
clearInterval(refreshInterval);
refreshInterval = undefined;
}
var refreshInterval,
i = 0;
<div onclick="start()">start</div>
<div onclick="stop()">stop</div>
You can keep the count variable in outer scope so it will not reset everytime you run start function
let refreshInterval = null;
let count = 0
function start() {
refreshInterval = setInterval(function() {
document.getElementById('value').innerText = count
count++;
}, 1000);
document.getElementById('start').disabled=true
}
function stop() {
clearInterval(refreshInterval)
document.getElementById('start').disabled=false
}
#counter{
padding: 0.25rem;
font-size: 1.25rem;
border: 1px solid black;
margin-bottom: 1rem;
}
<div id='counter'><span>counter:</span>
<span id='value'/>0</div>
<button onclick=start() id='start'>start</button>
<button onclick=stop() id='stop'>stop</button>

Setting a custom countdown timer without reinitiating -- for second countdown upon onClick

Setting a custom countdown timer without reinitiating -- for second countdown upon onClick
//Define your variable at start
var seconds,
countdownTimer,
//Add a variable to know that a new click will not be valid
counting = false;
function startItUp() {
var _count = function(){
var secondsInput=document.getElementById('seconds').value;
//If you have no value this will be 0
seconds=parseInt(secondsInput) || 0;
//The function does not pass as a string
countdownTimer=setInterval(secondPassed, 1000);
}
if(!counting){
counting = true;
_count();
}
/*
//If you want to restart the count, remove the previous code and put this
clearInterval(countdownTimer);
_count();
*/
}
var numIntiate=1;
function secondPassed() {
var minutes=Math.abs(Math.round((seconds - 30)/60));
var remainingSeconds=seconds % 60;
//If it is less than ten you will create an error if you have a zero as string
//You should put lineal if/else statement
document.getElementById('countdown').innerHTML=minutes+':'+(remainingSeconds<10?'0'+ remainingSeconds: remainingSeconds);
if (seconds==0) {
//Stop the interval using its var
clearInterval(countdownTimer);
document.getElementById('alert').innerHTML='Buzz Buzz';
seconds=0;
counting = false;
}
else {
seconds--;
}
}
body {
text-align: center;
}
#input-container {
padding: 25px 25px;
display: inline-block;
background-color: pink;
}
input {
text-align: center;
display: inline;
}
p {
display: inline-block;
padding-right: 10px;
font-weight: bold;
font-size: 20px;
}
#start-button {
margin-top: 20px;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<h1>Timer</h1>
<div class="fluid-row" id="input-container">
<div class="col-sm-12">
<h1 id="countdown"></h1>
<h1 id="alert"></h1>
</div>
<div class="col-sm-12">
<p>S</p>
<input type="number" id="seconds" placeholder="seconds">
</div>
<button class="btn btn-success" id="start-button" onclick="startItUp()">START</button>
</div>
I made a working basic structure for your needs, keeping it simple. This should help you, but you have to tweak it to include it in your project
var interval,
elapsed,
duration;
// This function should be called each time the user presses start:
var startCountDown = function (seconds) {
// set the duration in ms:
duration = seconds * 1000;
// reset the elapsed time to 0 ms:
elapsed = 0;
// start the count down:
interval = window.setInterval(countDown, 1000);
};
// Updates the counter and stops it when finished:
var countDown = function () {
// increase elapsed # of ms:
elapsed += 1000;
// this is the # of seconds left:
console.log((duration - elapsed) / 1000);
// Check if we are done, if so, stop the countdown:
if (elapsed === duration) window.clearInterval(interval);
};
// The onclick callback function should be a function
// that converts the user input to # of seconds and calls 'startCountDown' with that value:
document.getElementById("start-button").addEventListener("click", function () {
startCountDown(parseInt(document.getElementById("seconds").value));
});
if you remove the last part of registering the event handler, and say:
startCountDown(10);
you have a working example.

Categories

Resources