I want to create a simple eggtimer app. I created a timer and two buttons start and pause. However, since I'm a beginnner, I cannot work them right. When I hit the start button, the clock is running but if I hit the start button again, the setInterval is triggered again (which I don't want). Therefore, I used {once : true} with the event listener. But consequently when I use the pause button, I cannot resume the clock. I know I should store the event in a boolean, but I don't know how to do it correctly. Can you help me? I know the key things is setting the variable clockIsRunning in the right way. But how?
const startingMinutes = 25;
let time = startingMinutes * 60;
const startBtn = document.querySelector("#start-btn");
const pauseBtn = document.querySelector("#pause-btn");
const resetBtn = document.querySelector("reset-btn");
let interval;
let clockIsRunning = false;
// start button
startBtn.addEventListener("click", startCountdown, false);
const countdownEl = document.querySelector("#countdown");
function updateCountdown() {
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds < 10 ? "0" + seconds : seconds;
countdownEl.innerHTML = `${minutes}:${seconds}`;
time--;
clockIsRunning = true;
}
function startCountdown() {
if ((clockIsRunning = false)) {
interval = setInterval(updateCountdown, 1000);
}
}
// pause button
pauseBtn.addEventListener("click", function() {
clearInterval(interval);
});
<div id="display-container">
<div id="display-timer">
<p id="countdown">25:00</p>
</div>
<p id="display-logo">Focus</p>
</div>
<div id="buttons-container">
<button id="reset-btn">
<i class="fas fa-undo-alt"></i></ion-icon>Reset
</button>
<button id="start-btn"><i class="fas fa-play"></i></ion-icon>Start</button>
<button id="pause-btn"><i class="fas fa-pause"></i></ion-icon>Pause</button>
</div>
if...else take a condition, the code is executed if the boolean is true, not if false.
Knowing setInterval returns and integer (true) and clearInterval returns undefined (false), you can easily have a clock status by assign it to interval directly, thanks to #danh.
/* If clock is not running, start */
if(!interval) {
interval = setInterval(updateCountdown, 1000);
}
/* If clock is running, stop */
if(interval) {
interval = clearInterval(interval); // undefined
}
const startingMinutes = 25;
let time = startingMinutes * 60;
let interval;
const startBtn = document.querySelector("#start-btn");
const pauseBtn = document.querySelector("#pause-btn");
startBtn.addEventListener("click", startCountdown, false);
const countdownEl = document.querySelector("#countdown");
function updateCountdown() {
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds < 10 ? "0" + seconds : seconds;
countdownEl.innerHTML = `${minutes}:${seconds}`;
time--;
}
function startCountdown() {
if(!interval) {
updateCountdown();
interval = setInterval(updateCountdown, 1000);
}
}
// pause button
pauseBtn.addEventListener("click", function() {
if(interval) {
interval = clearInterval(interval);
}
});
<body>
<div id="display-container">
<div id="display-timer">
<p id="countdown">25:00</p>
</div>
</div>
<div id="buttons-container">
<button id="start-btn"><i class="fas fa-play"></i>Start/resume</button>
<button id="pause-btn"><i class="fas fa-pause"></i>Pause</button>
</div>
</body>
You have a few problems here. Your if statement in your startCountdown function assigns false to clockIsRunning, and so will always return false. What you should do is check for equality, i.e. use ==.
Then, if the clock isn't running, have the startCountdown function set the variable to true; this should not be done in the updateCountdown function, since this is run during the interval (i.e. after a second) and so could allow multiple intervals to be set if the Start button is clicked multiple times before the first interval elapses.
Also, in your event listener for the pause button, set clockIsRunning to false. Your code would look something like this:
// interval handler
function updateCountdown() {
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds < 10 ? "0" + seconds : seconds;
countdownEl.innerHTML = `${minutes}:${seconds}`;
time--;
}
// start button
function startCountdown() {
if ((clockIsRunning == false)) {
clockIsRunning = true;
interval = setInterval(updateCountdown, 1000);
}
}
// pause button
pauseBtn.addEventListener("click", function () {
clockIsRunning = false;
clearInterval(interval);
});
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'm trying to start a countdown function with an event listener because at the moment it starts automatically.
I found this code somewhere on the net and want to adjust it to my needs but I need some help.
I guess setInterval must be to blame. I guess it runs the function every second, right?
But I want it to start when I press the button, so I put the setInterval inside the updateCountdown body but weird things started to happen (that the clock started to work irregularly)
const startingMinutes = 25;
let time = startingMinutes * 60;
const startBtn = document.querySelector("#start-btn");
startBtn.addEventListener("click", updateCountdown);
const countdownEl = document.querySelector("#countdown");
function updateCountdown() {
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds < 10 ? "0" + seconds : seconds;
countdownEl.innerHTML = `${minutes}:${seconds}`;
time--;
}
setInterval(updateCountdown, 1000);
<div id="display-container">
<div id="display-timer">
<p id="countdown">25:00</p>
</div>
<p id="display-logo">Focus</p>
</div>
<div id="buttons-container">
<button id="reset-btn">
<i class="fas fa-undo-alt"></i></ion-icon>
</button>
<button id="start-btn"><i class="fas fa-play"></i></ion- icon></button>
<button id="pause-btn"><i class="fas fa-pause"></i></ion-icon></button>
</div>
It sounds like you want to start the countdown only when the user clicks the button. In that case, use addEventListener to add a function that calls setInterval, passing to that the function you actually want repeated. So it will look like this:
const startingMinutes = 25;
let time = startingMinutes * 60;
const startBtn = document.querySelector("#start-btn");
startBtn.addEventListener("click", startCountdown);
const countdownEl = document.querySelector("#countdown");
function updateCountdown() {
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds < 10 ? "0" + seconds : seconds;
countdownEl.innerHTML = `${minutes}:${seconds}`;
time--;
}
function startCountdown() {
setInterval(updateCountdown, 1000);
}
I'll leave it to you to sort out more subtle details like what should happen if the user clicks the button a second time.
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
How do I stop and reset this setInterval timer back to 00:00?
Setting clearInterval(timer); works for stopping the time but I can't figure out how to reset the time value back to 00:00.
Here's where I'm at:
HTML
<span class="timer">
<label id="minutes">00</label>:<label id="seconds">00</label>
</span>
JS
const minutesLabel = document.getElementById("minutes");
const secondsLabel = document.getElementById("seconds");
let timer = setInterval(setTime, 1000);
function setTime() {
++totalSeconds;
secondsLabel.innerHTML = pad(totalSeconds % 60);
minutesLabel.innerHTML = pad(parseInt(totalSeconds / 60));
}
function pad(val) {
let valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
stopTimer = () => {
clearInterval(timer);
timer = setInterval(setTime, 1000);
};
I'm using this timer in a game so based on the users state I need to be able start, stop, and restart the time back to 0.
What's happening now is that your variable keeps going up, even when you "reset" the timer.
Simply add
totalSeconds = 0;
to your stopTimer function. This will reset the variable to zero.
The variable totalSeconds keeps track of your time.
The name of your function stopTimer() - maybe it should be restartTimer - seems a bit misleading since you're restarting the timer there but basically just set totalSeconds to 0 like:
stopTimer = () => {
totalSeconds = 0;
clearInterval(timer);
timer = setInterval(setTime, 1000);
};
It's my first time using JS classes. I have a setInterval in a function but the function that is supposed to be repeating but I'm not sure why it's not.
class Stopwatch
{
constructor(isRunning, timer, timerTime)
{
this.isRunning = isRunning;
this.timer = timer;
this.timerTime = timerTime;
}
init()
{
// Put the element on the page with an id of start in a variable
// Do the same for the stop button and the reset button
const startButton = document.getElementById("start");
const stopButton = document.getElementById("stop");
const resetButton = document.getElementById("reset");
// Add an onclick handler to each of the buttons
// Use the functions startTimer, stopTimer and resetTimer
startButton.onclick = this.startTimer;
stopButton.onclick = this.stopTimer;
resetButton.onclick = this.resetTimer;
}
startTimer()
{
// if the timer is NOT running, start it
// call the function incrementTimer every second
// save the timer in a the timer variable
if(!this.isRunning)
{
this.isRunning = true;
this.timer = setInterval(this.incrementTimer, 1000);
}
}
incrementTimer()
{
// increment the timerTime
// calculate the number of minutes and seconds
this.timerTime++;
let minutes = Math.floor(this.timerTime / 60);
let seconds = this.timerTime % 60;
// write the minutes and seconds to the elements on the page
// use the function pad to make sure there's a leading 0 if necessary
document.getElementById("minutes").innerHTML = this.pad(this.minutes);
document.getElementById("seconds").innerHTML = this.pad(this.seconds);
}
pad(number)
{
// add a leading 0 if the number is < 10
if(number < 10)
number = "0" + number;
return number;
}
stopTimer()
{
// if the timer is running, stop it
if(this.isRunning)
{
isRunning = false;
timer = clearInterval(timer);
}
}
resetTimer()
{
// stop the timer
this.stopTimer();
// set the timerTime back to 0
this.timerTime = 0;
// write 00 to the elements on the page for minutes and seconds
document.getElementById("minutes").innerHTML = "00";
document.getElementById("seconds").innerHTML = "00";
}
}
let stopwatch = new Stopwatch(false, null, 0);
// When the page has finished loading, call the function init
window.onload = stopwatch.init();
When you set the onclick property of an element, the function you assign will always be called with the this value set to the element. Thus, the properties of this that you try to access will not be defined.
Instead of:
startButton.onclick = this.startTimer;
Use Function.prototype.bind to set the this value the function will be called with:
startButton.onclick = this.startTimer.bind(this);
Also, your code for incrementing the timer is incorrect:
let minutes = Math.floor(this.timerTime / 60);
let seconds = this.timerTime % 60;
document.getElementById("minutes").innerHTML = this.pad(this.minutes);
document.getElementById("seconds").innerHTML = this.pad(this.seconds);
this.minutes is undefined as you used let to declare minutes and seconds. Instead, just reference the variables using their names--i.e., minutes and seconds.
document.getElementById("minutes").innerHTML = this.pad(minutes);
document.getElementById("seconds").innerHTML = this.pad(seconds);
When you are stopping the timer, you forgot to add this in front of the property you were trying to access.
This:
if(this.isRunning){
isRunning = false;//isRunning is not defined as a local or global variables
timer = clearInterval(timer);//timer is not defined
}
Should be:
if(this.isRunning){
this.isRunning = false;
this.timer = clearInterval(this.timer);
}
Demo:
<button id="start">
Start
</button>
<button id="stop">
Stop
</button>
<button id="reset">
Reset
</button>
<span id="minutes"></span>:<span id="seconds"></span>
<script>
class Stopwatch
{
constructor(isRunning, timer, timerTime)
{
this.isRunning = isRunning;
this.timer = timer;
this.timerTime = timerTime;
}
init()
{
// Put the element on the page with an id of start in a variable
// Do the same for the stop button and the reset button
const startButton = document.getElementById("start");
const stopButton = document.getElementById("stop");
const resetButton = document.getElementById("reset");
// Add an onclick handler to each of the buttons
// Use the functions startTimer, stopTimer and resetTimer
startButton.onclick = this.startTimer.bind(this);
stopButton.onclick = this.stopTimer.bind(this);
resetButton.onclick = this.resetTimer.bind(this);
}
startTimer()
{
// if the timer is NOT running, start it
// call the function incrementTimer every second
// save the timer in a the timer variable
if(!this.isRunning)
{
this.isRunning = true;
this.timer = setInterval(this.incrementTimer.bind(this), 1000);
}
}
incrementTimer()
{
// increment the timerTime
// calculate the number of minutes and seconds
this.timerTime++;
let minutes = Math.floor(this.timerTime / 60);
let seconds = this.timerTime % 60;
// write the minutes and seconds to the elements on the page
// use the function pad to make sure there's a leading 0 if necessary
document.getElementById("minutes").innerHTML = this.pad(minutes);
document.getElementById("seconds").innerHTML = this.pad(seconds);
}
pad(number)
{
// add a leading 0 if the number is < 10
if(number < 10)
number = "0" + number;
return number;
}
stopTimer()
{
// if the timer is running, stop it
if(this.isRunning)
{
this.isRunning = false;
this.timer = clearInterval(this.timer);
}
}
resetTimer()
{
// stop the timer
this.stopTimer();
// set the timerTime back to 0
this.timerTime = 0;
// write 00 to the elements on the page for minutes and seconds
document.getElementById("minutes").innerHTML = "00";
document.getElementById("seconds").innerHTML = "00";
}
}
let stopwatch = new Stopwatch(false, null, 0);
// When the page has finished loading, call the function init
window.onload = stopwatch.init();
</script>