having difficulty stopping timer outside of loop. I don't really know why the setTimeout() has been helping the function work... and i know its not the most syntactically correct.. but wondering if someone can help point me as to how to be able to call it outside the function to stop the countdown, say if an action occurs before the timer, and want to call a stopCountdown() function?
function countdown(start){
setTimeout(setCountdown, 1000);
let startingMinutes = timerEl.innerHTML;
startingMinutes = start;
let time = startingMinutes * 60;
function setCountdown(){
const minutes = Math.floor(time/60);
let seconds = time % 60;
if(seconds < 10){
seconds = '0' + seconds
} else {
seconds
}
if(minutes <=0 && seconds <=0){
clearInterval(start);
console.log('timerOver')
} else{
setTimeout(setCountdown, 1000);
timerEl.innerHTML = (minutes + ':'+seconds)
time--;
}
}}
function stopCountdown(){
document.querySelector("#countdown").innerText = '0'
setTimeout(setCountdown(start));
}
Welcome to coding, I am trying my best to explain it. First, let me point out some of my opinion on your code
function countdown(start){
setTimeout(setCountdown, 1000);
let startingMinutes = timerEl.innerHTML;
startingMinutes = start;
// I am not sure why you firstly initializing startMinutes
// to the value of timerEl.innerHTML
//and then reassign the value of startMinutes to variable start next line
let time = startingMinutes * 60;
function setCountdown(){
const minutes = Math.floor(time/60);
let seconds = time % 60;
if(seconds < 10){
seconds = '0' + seconds
} else {
seconds
}
if(minutes <=0 && seconds <=0){
clearInterval(start); // you are using setTimeout, not setInterval
console.log('timerOver')
} else{
setTimeout(setCountdown, 1000);
timerEl.innerHTML = (minutes + ':'+seconds)
time--;
}
}}
function stopCountdown(){
document.querySelector("#countdown").innerText = '0'
setTimeout(setCountdown(start));
// when calling stopCountdown(), what is the value of start?
// you can't access function setCountdown inside function stopCountdown
}
If my guess is correct, you want to make a timer and then you can make it stop when calling a stopCountdown function, right?
For a timer, it is simply asking javascript to - 1 seconds for every 1000 ms passed. So we can write a function which -1 seconds and ask JS to run it every 1000ms, right?
In this case, you should use setInterval but not setTimeout (setTimeout can also make a timer, I will also show you). The difference is that setTimeout calls a function ONCE after X milliseconds and setInterval will run a function EVERY X milliseconds.
Here is the code
let countdownIntervalId // get the countdownIntervalId outside by first declearing a variable to catch the id
function countdown(start) { // assume the unit of start is minute
console.log("countdown called, minutes =" + start)
// add code here to change the innerHTML of the timer if you want
let secondsToCount = start * 60; //Converting minutes to seconds
countdownIntervalId = setInterval(() => {
timer()
}, 1000); // starting to count down
function timer() { // run every seconds
const minutes = Math.floor(secondsToCount / 60);
let seconds = secondsToCount - minutes*60;
console.log("counter= " + minutes + ':' + `${seconds}`.padStart(2, '0'))
secondsToCount = secondsToCount-1;
if (minutes <= 0 && seconds <= 0) {
clearInterval(countdownIntervalId); // clearInterval
console.log('timerOver')
}
}
}
function stopCountdownOutside(){
if(countdownIntervalId){
clearInterval(countdownIntervalId)
}
}
countdown(2) //countdown 2 mins
You can stop the counter by calling stopCountdownOutside(), you can test on Chrome console. This is because we are passing the intervalId to the countdownIntervalId which is declare outside the function. so we can simply call clearInterval(countdownIntervalId) to stop it
For using the setTimeout
let countdownTimeoutId// get the countdownIntervalId outside by first declearing a variable to catch the id
function countdown(start) { // assume the unit of start is minute
console.log("countdown called, minutes =" + start)
// add code here to change the innerHTML of the timer if you want
let secondsToCount = start * 60; //Converting minutes to seconds
countdownTimeoutId = setTimeout(() => {
timer()
}, 1000); // starting to count down
function timer() { // run every seconds
const minutes = Math.floor(secondsToCount / 60);
let seconds = secondsToCount - minutes*60;
console.log("counter= " + minutes + ':' + `${seconds}`.padStart(2, '0'))
secondsToCount = secondsToCount-1;
if (minutes <= 0 && seconds <= 0) {
clearTimeout(countdownTimeoutId); // clearTimeout
console.log('timerOver')
}else{
countdownTimeoutId = setTimeout(timer,1000)
}
}
}
function stopCountdownOutside(){
if(countdownTimeoutId){
clearTimeout(countdownTimeoutId)
}
}
countdown(1) //countdown 2 mins
you can try to refactor my code to a more clean version, happy coding
Related
Hi everyone I am new to JavaScript and was watching some tutorials on the stopwatch in javascript I manage to understand most of the code but still have some questions. I was wondering can someone help explain the purpose of the interval being null. and how did this code work? how did it prevent the function starts
// Global variables
const time_el = document.querySelector('.watch .time');
const start_btn = document.getElementById('start');
const stop_btn = document.getElementById("stop");
const reset_btn = document.getElementById("reset");
let seconds = 0;
let interval = null;
// Event listeners
start_btn.addEventListener('click', start);
stop_btn.addEventListener("click", stop);
reset_btn.addEventListener("click", reset);
// Update the timer
function timer() {
seconds++;
// Format our time
let hrs = Math.floor(seconds / 3600);
let mins = Math.floor((seconds - (hrs * 3600)) / 60);
let secs = seconds % 60;
if (secs < 10) secs = '0' + secs;
if (mins < 10) mins = "0" + mins;
if (hrs < 10) hrs = "0" + hrs;
time_el.innerText = `${hrs}:${mins}:${secs}`;
}
function start() {
if (interval) {
return
}
interval = setInterval(timer, 1000);
}
function stop() {
clearInterval(interval);
interval = null;
}
function reset() {
stop();
seconds = 0;
time_el.innerText = '00:00:00';
}
<div class="watch">
<div class="time"></div>
</div>
<button id="start">start</button>
<button id="stop">stop</button>
<button id="reset">reset</button>
from running again when I clicked more than once?
function start () {
if (interval) {
return
}
interval = setInterval(timer, 1000);
}
I pasted the whole JS code to give a better context
In javascript null evaluates to false (falsy values): js-falsy, there are also values that evaluate to true (truthy values): js-truthy, so they initialize the variable interval to null so the code knows whether to start the timer, if it doesn't evaluate to false (falsy) the timer has been started and setInterval has been called, so it returns immediately on another click of the start button.
setInterval returns an Interval ID which is a unique identifier: setInterval, this is what is stored in interval on initial start button click, so on subsequent clicks, the variable interval evaluates to true (truthy).
Added a code snippet to demonstrate what I tried to describe in points 1 and 2.
let interval = null;
//Check to see if interval evaluates to false(falsy)
if(!interval){
console.log("interval evaluated to false(falsy)");
}
//set interval to the ID of call to setInterval()
interval = setInterval(() => {
console.log("setInterval() called and interval set to my ID");
}, 1000000);
//Check to see if interval evaluates to true(truthy)
if(interval){
console.log("setInterval ID: " + interval);
console.log("interval evaluated to true(truthy)");
}
I want to make 3 countdown timers where the next one starts when the last one ends (the first timer will start counting down the time automatically, but the second timer will only start when the first one reaches 0:00 and the third one will only start when the second one reaches 0:00).
I found this code for a countdown timer:
function countDown() {
var seconds = 60;
var mins = 5;
function clickClock() {
var counter = document.getElementById("countdown1");
var currentMinutes = mins - 1;
seconds--;
counter.innerHTML = currentMinutes.toString() + ":" + (seconds < 10 ? "0" : "") + String(seconds);
if(seconds > 0) {
setTimeout(clickClock, 1000);
} else {
if(mins > 1) {
countDown(mins-1);
}
}
}
clickClock();
}
countDown();
On my HTML, I have 3 spans, each with a unique ID (#countdown1, #countdown2, #countdown3)
I have tried passing in an parameter to the clickClock() function called counter so that whenever I called the function I could enter the id of the element I wanted to affect, didn't work.
I could just make 2 other functions that would do exactly the same thing but would change the counter variable, but I'd like to avoid repeating unnecessary things in my code.
How could this be done?
Any help is appreciated :)
I'd do something like this:
/////////// USAGE
const timersDurationInMilliseconds = 1000 * 5; // for 5 minutes do: 1000 * 60 * 5
// Render initial timer content
RenderTimer('countdown1', timersDurationInMilliseconds);
RenderTimer('countdown2', timersDurationInMilliseconds);
RenderTimer('countdown3', timersDurationInMilliseconds);
// Start countdown, then start another ones
countDown(timersDurationInMilliseconds, 'countdown1')
.then(() => countDown(timersDurationInMilliseconds, 'countdown2'))
.then(() => countDown(timersDurationInMilliseconds, 'countdown3'))
.then(() => alert('All timers finished!'));
/////////// REQUIRED METHODS
function countDown(durationInMilliseconds, elementId) {
return new Promise((resolve, reject) => {
const updateFrequencyInMilliseconds = 10;
const currentTimeInMilliseconds = new Date().getTime();
const endTime = new Date(currentTimeInMilliseconds + durationInMilliseconds);
function updateTimer(elementId) {
let timeLeft = endTime - new Date();
if (timeLeft > 0) {
// We're not done yet!
setTimeout(updateTimer, updateFrequencyInMilliseconds, elementId);
} else {
// Timer has finished!
resolve();
// depending on update frequency, timer may lag behind and stop few milliseconds too late
// this will cause timeLeft to be less than 0
// let's reset it back to 0, so it renders nicely on the page
timeLeft = 0;
}
RenderTimer(elementId, timeLeft);
}
updateTimer(elementId);
});
}
function padNumber(number, length) {
return new String(number).padStart(length || 2, '0'); // adds leading zero when needed
}
function RenderTimer(elementId, timeLeft) {
const hoursLeft = Math.floor(timeLeft / 1000 / 60 / 60 % 60);
const minutesLeft = Math.floor(timeLeft / 1000 / 60 % 60);
const secondsLeft = Math.floor(timeLeft / 1000 % 60);
const millisecondsLeft = timeLeft % 1000;
const counterElement = document.getElementById(elementId);
counterElement.innerHTML = `${padNumber(hoursLeft)}:${padNumber(minutesLeft)}:${padNumber(secondsLeft)}.${padNumber(millisecondsLeft, 3)}`;
}
<p>First countdown: <span id="countdown1"></span></p>
<p>Second countdown: <span id="countdown2"></span></p>
<p>Third countdown: <span id="countdown3"></span></p>
If you want to only display minutes and seconds, you can adjust that behavior in RenderTimer method.
If you don't plan on displaying milliseconds to the user, you may want to change how frequent the timer is updated and rendered on the page by adjusting the updateFrequencyInMilliseconds variable (e.g. from 10ms to 1000ms).
Im working on code for a simple stopwatch. Last obstacle for me is reset the time to zero. The function resetTimer is where i am trying to implement the code. So the webpage will display a page with a timer and three buttons; stop, start and reset. When a user clicks the reset button, the timer is supposed to reset back to zero. I have been having trouble trying to make it work. Any help/ideas would be clutch.
I hope i made myself clear. Again i am trying to make the timer reset to 00:00:00
window.onload = function () {
//grab possible elements needed
const timerEl = document.getElementById("timer-text")
const startBtn = document.getElementById("start")
const restartBtn = document.getElementById("restart");
const stopBtn = document.getElementById('stop');
//hold variables of time and set to 0
let hours = parseInt('0');
let minutes = parseInt('0');
let seconds = parseInt('0');
let time;
function makeTwoNumbers(num) {
if (num < 10) {
return "0" + num
}
return num
}
//timer
let timer = () => {
seconds++
//console.log(seconds)
if (seconds == 60) {
minutes++
seconds = 0;
hours = 0
}
if (minutes == 60) {
hours++
minutes = 0;
hours = 0;
}
timerEl.textContent = makeTwoNumbers(hours)+ ": " + makeTwoNumbers(minutes) + ": " + makeTwoNumbers(seconds);
}
let runTheClock;
//timer is running
function runTimer() {
runTheClock = setInterval(timer, 20);;
}
function stopTimer() {
clearInterval(runTheClock)
}
//function will reset timer
function resetTimer() {
time--;
timerEl.textContent;
if (time === 0) {
stopTimer();
time = 0
}
}
restartBtn.addEventListener("click", function () {
resetTimer();
})
//button will pause the timer
stopBtn.addEventListener("click", function () {
stopTimer();
})
//button will start the timer
startBtn.addEventListener("click", function () {
runTimer();
})
}
Here's a fixed and slightly refactored version.
<html>
<body>
<div id="timer-text"></div>
<button id="start">start</button>
<button id="restart">restart</button>
<button id="stop">stop</button>
</body>
<script>
const timerEl = document.getElementById("timer-text")
const startBtn = document.getElementById("start")
const restartBtn = document.getElementById("restart");
const stopBtn = document.getElementById('stop');
let runTheClock;
let seconds = 0;
render(seconds);
function makeTwoNumbers(num) {
return ((num < 10) ? "0" : "") + num;
}
function tick() {
seconds++;
render(seconds);
}
function render(secs) {
const hours = Math.floor(secs / 3600);
const minutes = Math.floor(secs / 60) - (hours * 60);
const seconds = secs % 60;
const val = [hours, minutes, seconds].map(makeTwoNumbers).join(":");
console.log(val);
timerEl.textContent = val;
}
function runTimer() {
runTheClock = setInterval(tick, 1000);
}
function stopTimer() {
clearInterval(runTheClock)
}
function resetTimer() {
seconds = 0;
render(seconds);
}
restartBtn.addEventListener("click", resetTimer);
stopBtn.addEventListener("click", stopTimer);
startBtn.addEventListener("click", runTimer);
</script>
</html>
In the reset function it just sets seconds back to 0 and sets the textContent value so it appears on the page. I separated out the calculating and drawing of the time into a render fucntion, so it can be reused whenever it needs to be re-rendered.
To explain the render function.
We only need to store the number of seconds as a persistent variable between the periodic function calls. We can derive hours and minutes from it. This makes it much less error prone than trying to increment hours and minutes as well.
To calculate hours we just divide seconds by 3600 (or 60 x 60 the number of seconds in an hour) and round down.
To calculate minutes we can calculate the number of total minutes (seconds / 60 and round down) then subtract the number of minutes in the hours value we calculated (hours * 60).
For seconds we use modulus or % which is just a fancy word for remainder. So seconds % 60 gives us the remainder value of seconds / 60. For example 61 % 60 = 1. This isn't the only way these values could be calculated.
To build the display string. I just put all of the hours, minutes and seconds in an array. Then used the map method, which applies the function makeTwoNumbers to all of the values. I then used the join method to join all the strings using the delimiter :. It just saves some typing and means you only reference makeTwoNumbers once, making it less work to use a different function later if you want to.
Hope that helps.
I realized that you could simply reset seconds, hours, and minutes to 0 and use a variable true. This would reset it entirely to 0. I couldnt believe how simple it was
I'm having a problem get this countdown timer to stop at zero so the time won't show as a negative value. The console.log is getting called and works fine but for some reason the clearInterval() is not. This is driving me crazy and I'm close to quitting.
const timerContainer = document.getElementById('timerContainer');
const THREEMINUTES = 60 * 0.1;//5 seconds for testing
startTimer(THREEMINUTES, timerContainer);
function startTimer(duration, display) {
let start = Date.now();
let diff, min, sec;
let timer = () => {
diff = duration - (((Date.now() - start) / 1000) | 0);
//use bitwise to truncate the float
min = (diff / 60) | 0;
sec = (diff % 60) | 0;
min = min < 10 ? '0' + min : min;
sec = sec < 10 ? '0' + sec : sec;
display.textContent = min + ':' + sec;
if (diff <= 0) {
stopTimer();
submit.disabled = 'true';
};
};
//call timer immediately otherwise we wait a full second
timer();
setInterval(timer, 1000);
function stopTimer() {
clearInterval(timer);
console.log("time's up", diff);
};
}
<div id="timerContainer"></div>
You are not saving the result of setInterval(timer, 1000);
you should use this:
let timerId;
timer();
timerId = setInterval(timer, 1000);
function stopTimer() {
clearInterval(timerId);
console.log("time's up", diff)
};
As you might see, the result of setInterval is a number (object in node), and all you then need to do is pass that value to clearInterval thus we save the value in the variable timerId for reference.
Don't pass the function that you want stopped to clearInterval().
Pass a reference to the timer that you started, so you need to make sure that when you start a timer, you capture a reference to the ID that will be returned from it.
// Function that the timer will invoke
function callback(){
. . .
}
// Set up and initiate a timer and capture a reference to its unique ID
var timerID = setInterval(callback, 1000);
// When needed, cancel the timer by passing the reference to it
clearInterval(timerID);
The code is fixed make sure you fix your submit button code.
You should first assign the value of setInterval to a variable. That variable is used while calling clearInterval which infact clears the interval.
const timerContainer = document.getElementById('timerContainer');
const THREEMINUTES = 60 * 0.1;//5 seconds for testing
startTimer(THREEMINUTES, timerContainer);
var timer = null;
function startTimer(duration, display) {
let start = Date.now();
let diff, min, sec;
let timer = () => {
diff = duration - (((Date.now() - start) / 1000) | 0);
//use bitwise to truncate the float
min = (diff / 60) | 0;
sec = (diff % 60) | 0;
min = min < 10 ? '0' + min : min;
sec = sec < 10 ? '0' + sec : sec;
display.textContent = min + ':' + sec;
if (diff <= 0) {
stopTimer();
submit.disabled = 'true';
};
};
//call timer immediately otherwise we wait a full second
timer();
timer = setInterval(timer, 1000);
function stopTimer() {
clearInterval(timer);
console.log("time's up", diff);
};
}
I am trying to add functionality to a html/js stopwatch, but i have a problem with setTimeout() function. Why is it only executed once for me, but in this video:
https://www.youtube.com/watch?annotation_id=annotation_3059475893&feature=iv&src_vid=iSLWtGAw1Ic&v=gpFPppFU8s8
it is executed until someone stops it. Yeah I know I can use setInterval, but I'm just curious.
here is my code:
let running = false;
let time = 0;
function start() {
running = true;
timer();
}
function stop() {
running = false;
}
function reset() {
running = false;
time = 0;
document.getElementById("stw").innerHTML = "00:00:00";
}
function timer() {
if (running) {
setTimeout(function() {
time++;
let min = Math.floor(time / 100 / 60);
let sec = Math.floor(time / 100) % 60;
let mSec = time % 100;
if (min < 10) {
min = "0" + min;
}
if (sec < 10) {
sec = "0" + sec;
}
if (mSec < 10) {
mSec = "0" + mSec;
}
document.getElementById("stw").innerHTML =
min + ":" + sec + ":" + mSec;
}, 10);
}
}
When you use setTimeout() to create a loop, inside the function setTimeout() calls, you have to call setTimeout() again.
Here is a basic loop function that does it:
const loopFunc = () => {
console.log('loop');
setTimeout(loopFunc, 1000);
};
loopFunc(); // kick it off
In the video (around 2 minutes in), timer() calls itself recursively. Your code does not.
setTimeout() only calls its callback function once.
There are two ways to to have it call multiple times:
Use setInterval()
Use a recursive function to restart setTimeout() over and over again.
Example:
function timer() {
// do something
// call this function again in 10 milliseconds
setTimeout(timer, 10);
}