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>
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)");
}
The problem I have here is when I try to implement the audio is that it keeps on playing every second, I have included the source for the audio code at the very bottom if you may need it.
let time = startingMinutes * 60;
const countdownEl = document.getElementById('Countdown');
setInterval(updateCountdown, 1000);
function updateCountdown () {
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds <10 ? '0' + seconds : seconds;
countdownEl.innerHTML = `${minutes}:${seconds}`;
time--;
time = time < 0 ? 0 : time;
}
const audio = new Audio();
audio.src = "alarm.mp3"; ```
You need to clear setInterval once timer reaches 0 and also need to put the audio code inside updateCountdown function when timer reaches 0. Additionally you can use a global variable to track if audio is played or not and use it conditionally. Check the code.
document.addEventListener('DOMContentLoaded', function() {
let startingMinutes = 1; // one min
let time = startingMinutes * 60;
var audioPlayed = false; // audio is not played - create a global variable to track if audio is already played (optional)
const countdownEl = document.getElementById('Countdown');
const counterInterval = setInterval(updateCountdown, 1000);
function updateCountdown () {
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds <10 ? '0' + seconds : seconds;
countdownEl.innerHTML = `${minutes}:${seconds}`;
time--;
time = time < 0 ? 0 : time;
if(!time && !audioPlayed) {
const statusEl = document.getElementById('Status');
var status = "timer reaches 0";
// prefered - clear interval, so once timer reaches zero, there will be no more call to updateCountdown
clearInterval(counterInterval);
status += ", clear setInterval";
// this is optional, in case the code above not work for any reason
// set the global var audioPlayed to true to indicate audio is played
audioPlayed = true;
status += ", audio play starts";
Status.innerHTML = status;
// run your audio
// const audio = new Audio();
// audio.src = "alarm.mp3";
}
}
});
<html>
<span id="Countdown"></span> <span id="Status"></span>
</html>
For the people viewing this question and would like an answer. Please note this audio will keep on playing as the timer stays at 0
const startingMinutes= 1;
let time = startingMinutes * 60;
const countdownEl = document.getElementById('Countdown');
setInterval(updateCountdown, 1000);
function updateCountdown () {
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds <10 ? '0' + seconds : seconds;
countdownEl.innerHTML = `${minutes}:${seconds}`;
time--;
time = time < 0 ? 0 : time;
if (time <=0) {audio.play()};
}
const audio = new Audio();
audio.src = "alarm.mp3";
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);
});
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);
};
I'm making a game that has a timer in it.
What I'm trying to do is -- when the game starts via gameState.init(), the timer starts through timer.counter("start") -- but when the "restart" button is clicked, the timer stops and resets through timer.counter("reset").
The timer does reset back to 0, but it keeps counting and not getting cleared.
Appreciate any insight I can get. Thanks in advance!
var gameState = {
init: function(){
var difficultyLevel = document.querySelector('input[name="level"]:checked').value;
conditions.difficulty(difficultyLevel);
startFrame.classList.remove("active");
shuffleCards();
timer.counter("start");
display.moves(movesAllowed);
},
restart: function(){
startFrame.classList.add("active");
reset.allCards(cards);
shuffleCards();
timer.counter("reset");
matchCount = 0;
totalMoves = 0;
movesAllowed = 0;
timeAllowed = 0;
time = 0;
}
}
var timer = {
counter: function(status){
var clock = document.querySelector(".timer");
var incrementTime = setInterval(function(){
time++;
var minutes = Math.floor(time / 60);
var seconds = Math.floor(time % 60);
if(seconds < 10){
clock.innerText = minutes + ":0" + seconds;
} else {
clock.innerText = minutes + ":" + seconds;
}
}, 1000);
var stopTime = function(){
clearInterval(incrementTime);
}
if(status === "start"){
alert("counting");
}
if(status === "reset"){;
alert("reset");
stopTime();
}
}
}
Two issues:
The variable that holds the interval, incrementTime, is local to the counter function. Once the counter function ends, incrementTime gets garbage collected, because no reference to the interval remains anymore. You need the interval variable to be persistent instead.
You're setting a new interval every time counter is called. You should probably only set an interval when status is start, otherwise the old interval will continue running and won't be stoppable (reassigning the interval a setInterval is assigned to doesn't clear the interval):
let interval; // <---- Persistent
var timer = {
counter: function(status){
var clock = document.querySelector(".timer");
if (status === 'start') {
interval = setInterval(() => { // <---- Assign to persistent variable
time++;
var minutes = Math.floor(time / 60);
var seconds = Math.floor(time % 60);
if(seconds < 10){
clock.innerText = minutes + ":0" + seconds;
} else {
clock.innerText = minutes + ":" + seconds;
}
}, 1000);
alert("counting");
} else if(status === "reset"){
var stopTime = function(){
clearInterval(interval); // <---- Clear persistent variable
}
alert("reset");
stopTime();
}
}
}
(It would also be a bit more elegant for the stopTime and interval functions to be persistent, rather than being re-created every time they're needed; eg, you might assign them to properties of timer)