How to save countup timer upon browser refresh? - javascript

I am new to stack overflow and javascript. My first task was to create a count up timer. My code works, but if the browser is refreshed I lose the timer spot. I've been reading on using local storage to save the spot, but I am not sure on how to add that into my current code. Anyone willing to lend a hand on this? This is what my current javascript looks like in my html file.
window.onload = () => {
let day = 0;
let hour = 0;
let minute = 0;
let seconds = 0;
let totalSeconds = 0;
let intervalId = null;
function startTimer() {
++totalSeconds;
day = Math.floor(totalSeconds/86400);
hour = Math.floor(totalSeconds /3600);
minute = Math.floor((totalSeconds - hour*3600)/60);
seconds = totalSeconds - (hour*3600 + minute*60);
document.getElementById("day").innerHTML = day;
document.getElementById("hour").innerHTML = hour;
document.getElementById("minute").innerHTML = minute;
document.getElementById("seconds").innerHTML = seconds;
}
document.getElementById('start-btn').addEventListener('click', () => {
intervalId = setInterval(startTimer, 1000);
})
document.getElementById('stop-btn').addEventListener('click', () => {
if (intervalId)
clearInterval(intervalId);
});
document.getElementById('reset-btn').addEventListener('click', () => {
totalSeconds = 0;
document.getElementById("day").innerHTML = '0';
document.getElementById("hour").innerHTML = '0';
document.getElementById("minute").innerHTML = '0';
document.getElementById("seconds").innerHTML = '0';
});
}

First of all, instead of storing the seconds elapsed, you should store when the timer started and then calculate the seconds elapsed.
Here, if the value for "start-timestamp" is present in the Local Storage, startTimestamp is set to it and the timer is activated.
When the timer is started, startTimestamp is set to the current time and stored in the Local Storage, and when it's stopped, "start-timestamp" is removed from the Local Storage.
let day = 0;
let hour = 0;
let minute = 0;
let seconds = 0;
let startTimestamp = 0;
let intervalId = null;
function updateTimer() {
let totalSeconds = (Date.now() - startTimestamp) / 1000;
day = Math.floor(totalSeconds/86400);
hour = Math.floor(totalSeconds /3600);
minute = Math.floor((totalSeconds - hour*3600)/60);
seconds = totalSeconds - (hour*3600 + minute*60);
document.getElementById("day").innerHTML = day;
document.getElementById("hour").innerHTML = hour;
document.getElementById("minute").innerHTML = minute;
document.getElementById("seconds").innerHTML = seconds;
}
{
const _startTimestamp = localStorage.getItem("start-timestamp");
if (_startTimestamp) {
startTimestamp = Number(_startTimestamp);
intervalId = setInterval(updateTimer, 1000);
}
}
document.getElementById('start-btn').addEventListener('click', () => {
if (!intervalId) {
startTimestamp = Date.now();
localStorage.setItem("start-timestamp", startTimestamp);
intervalId = setInterval(updateTimer, 1000);
}
})
document.getElementById('stop-btn').addEventListener('click', () => {
if (intervalId) {
clearInterval(intervalId);
localStorage.removeItem("start-timestamp");
}
});
document.getElementById('reset-btn').addEventListener('click', () => {
totalSeconds = 0;
document.getElementById("day").innerHTML = '0';
document.getElementById("hour").innerHTML = '0';
document.getElementById("minute").innerHTML = '0';
document.getElementById("seconds").innerHTML = '0';
});

For sessionStorage try the following to check if totalSeconds already set and a valid number .. set totalSeconds to what is stored in sessionStorage otherwise set to 0:
let tmpSession = parseInt(sessionStorage["totalSeconds"] ?? 0);
let totalSeconds = (isFinite(tmpSession))? tmpSession : 0;
Then in startTimer() after incrementing totalseconds write the value to sessionStorage, like so :
sessionStorage.setItem("totalSeconds",totalSeconds);

Related

is there a way to somehow hold a keypress in js

So what I'm trying to do is to self replicate a Rubik's cube timer like cstimer.net
What I do is detect a spacebar key press and it is meant to start a timer, however it only adds 1 millisecond at a press instead of a continuous timer, what I'm thinking is to find a way to continuously hold the spacebar if the javascript file, how do I fix this?
const minute = document.getElementById("minute");
const second = document.getElementById("second");
const millisecond = document.getElementById("millisecond");
let ms = 0;
let sec = 0;
let min = 0;
const start = () => console.log("start")
document.addEventListener('keypress', function(event) {
if (event.code === "Space") {
timeoutId = setTimeout(function() {
ms = parseInt(ms);
sec = parseInt(sec);
min = parseInt(min);
ms++;
if (ms == 100) {
sec = sec + 1;
ms = 0;
}
if (sec == 60) {
min = min + 1;
sec = 0;
}
if (ms < 10) {
ms = '0' + ms;
}
if (sec < 10) {
sec = '0' + sec;
}
if (min < 10) {
min = '0' + min;
}
minute.innerHTML = min;
second.innerHTML = sec;
millisecond.innerHTML = ms;
start();
}, 10);
}
});
<span id="minute"></span>
<span id="second"></span>
<span id="millisecond"></span>
setTimeout only triggers the timer function once; instead you should use requestAnimationFrame to update the timer every repaint.
Let's also fix the unreliableness of the timer implementation as mentioned by #gre_gor. Instead of adding to the ms counter each time, store the initial time (via Date.now()) and then calculate the elapsed milliseconds each time.
const minute = document.getElementById("minute");
const second = document.getElementById("second");
const millisecond = document.getElementById("millisecond");
let initialTime;
function timer() {
let ms = Date.now() - initialTime;
let sec = Math.floor(ms / 1000);
ms = ms % 1000;
let min = Math.floor(sec / 60);
sec = sec % 60;
minute.innerHTML = min;
second.innerHTML = sec;
millisecond.innerHTML = ms;
requestAnimationFrame(timer);
}
document.addEventListener('keypress', function(event) {
if (event.code === "Space") {
initialTime = Date.now();
requestAnimationFrame(timer);
}
});
<span id="minute"></span>
<span id="second"></span>
<span id="millisecond"></span>
If you need to stop the timer, it's a bit trickier for a rAF loop than for setTimeout - see How to stop a requestAnimationFrame recursion/loop?.
This one is more precise and has animation built in. (You could be even more precise using function like performance.now())
const timer = document.getElementById("timer");
var start_time = null;
var state = "paused";
function draw() {
if (state == "playing") {
var diff = Date.now() - start_time
var time = (new Date(diff).toISOString().slice(11, 23));
timer.innerHTML = time;
}
}
function loop() {
draw();
requestAnimationFrame(loop)
}
loop();
document.addEventListener('keypress', function(event) {
if (event.code === "Space") {
if (state == "paused") {
state = "playing";
start_time = Date.now()
return;
}
if (state == "playing") {
state = "paused";
start_time = null;
return;
}
}
});
#timer {
font-size:48px;
}
Press [space]<br>
<span id="timer"></span>

Javascript Continue From a Specific Time

I have a timer and a time. The time format is like the currentTime variable in my code. I want to get the currentTime time and add +1 to it every second keeping the same time format. Also I would like to subtract the currentTime + 1 - the total hours but keeping it real time with setInterval. Hope I am clear about my questions. Thanks.
HTML
<button id="start">START</button>
<button id="pause">PAUSE</button>
<div id="output"></div>
Javscript
const startTimeButton = document.querySelector("#start")
const pauseTimeButton = document.querySelector("#pause")
const output = document.querySelector("#output");
let currentTime = "12: 42: 17";
let totalHours = 30;
let seconds = 0;
let interval = null;
const timer = () => {
seconds++;
// Get hours
let hours = Math.floor(seconds / 3600);
// Get minutes
let minutes = Math.floor((seconds - hours * 3600) / 60);
// Get seconds
let secs = Math.floor(seconds % 60);
if (hours < 10) {
hours = `0${hours}`;
}
if (minutes < 10) {
minutes = `0${minutes}`;
}
if (secs < 10) {
secs = `0${secs}`;
}
return `${hours}:${minutes}:${secs}`;
};
startTimeButton.addEventListener("click", () => {
pauseTimeButton.style.display = "flex";
startTimeButton.style.display = "none";
console.log("START TIME CLICKED");
if (interval) {
return;
}
interval = setInterval(timer, 1000);
});
pauseTimeButton.addEventListener("click", () => {
pauseTimeButton.style.display = "none";
startTimeButton.style.display = "flex";
console.log("PAUSE TIME CLICKED");
clearInterval(interval);
interval = null;
});
// Here is an example of what I would like to achive
// currentTime + 1 (each second)
// output.innerHTML = (parseInt(currentTime) + 1) - totalHours;

How would I get this stopwatch to continue running after stop-starting instead of resetting to 00:00:00 upon stop-starting?

I have a stopwatch for a small tool here, I received most of the code from a previous question and I was going about trying to implement it, I began breaking it down and trying to understand it.
So far I think I understand most of it (Still some bits I am researching); however I was trying to adapt the code to my tool.
My requirements:
A start/stop button (a single button) - the value will change depending on if the timer is running or not.
A reset button - this will simply reset the timer to 00:00:00 and if the tool is running it will also stop it.
So far, the reset button is not configured, this is fine. The start and stop button works; however say I stopped the timer, and then started it again without resetting it, the timer just begins at 00:00:00 again, it will not continue from where it was paused.
It would be greatly appreciated if anyone would be able to explain how I could do this? I have tried the following:
Storing 'differenceInMillis for each loop of updateTimer() in a global variable, then subtracting the value from startTime = Date.now() each time the timer is restarted (This was suggested by a user in a previous question), I could not get this to work.
The code I have so far -
HTML (buttons and clock):
const outputElement = document.getElementById("outputt");
var startTime = 0;
var running = 0;
var splitcounter = 0;
function startstop() {
if (running == 0) {
running = 1;
startTime = Date.now();
startstopbutton.value = 'Stop';
document.getElementById("outputt").style.backgroundColor = "#2DB37B";
updateTimer();
} else {
running = 0;
// logTime();
startstopbutton.value = 'Start';
document.getElementById("outputt").style.backgroundColor = "#B3321B";
}
}
function updateTimer() {
if (running == 1) {
let differenceInMillis = Date.now() - startTime;
let {
hours,
minutes,
seconds
} = calculateTime(differenceInMillis);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
requestAnimationFrame(updateTimer);
}
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 60;
let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
let seconds = Math.floor(milliS / SECONDS) % RESET;
return {
hours,
minutes,
seconds
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
<input id="startstopbutton" class="buttonZ" style="width: 120px;" type="button" name="btn" value="Start" onclick="startstop();">
<input id="resetbutton" class="buttonZ" style="width: 120px;" type="button" name="btnRst" id='btnRst' value="Reset" onclick="resetclock();" />
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>
UPDATE new version - does not work however - session storage does not work at SO so will have to test elsewhere
const outputElement = document.getElementById("outputt");
var startTime = 0;
var running = 0;
var splitcounter = 0;
function startstop() {
if (running == 0) {
running = 1;
startTime = new Date(sessionStorage.getItem("time"))
if (isNaN(startTime)) startTime = Date.now();
startstopbutton.value = 'Stop';
document.getElementById("outputt").style.backgroundColor = "#2DB37B";
updateTimer();
} else {
running = 0;
logTime();
startstopbutton.value = 'Start';
document.getElementById("outputt").style.backgroundColor = "#B3321B";
}
}
function updateTimer() {
if (running == 1) {
let differenceInMillis = Date.now() - startTime;
sessionStorage.setItem("time", differenceInMillis)
let {
hours,
minutes,
seconds
} = calculateTime(differenceInMillis);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
requestAnimationFrame(updateTimer);
}
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 60;
let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
let seconds = Math.floor(milliS / SECONDS) % RESET;
return {
hours,
minutes,
seconds
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
If you MUST use a date object, you will need to change
startTime = Date.now();
to
startTime = new Date(sessionStorage.getItem("time"))
if (isNaN(startTime)) startTime = Date.now();
and save the time
let differenceInMillis = Date.now() - startTime;
sessionStorage.setItem("time",differenceInMillis)
If not, use a counter instead of a date object
Also when you use a toggle as boolean, make it and use it as a boolean (good practice - not mandatory for this issue)
const outputElement = document.getElementById("outputt");
let counter = 0,
running = false,
splitcounter = 0,
lastTime = 0;
function startstop() {
running = !running;
startstopbutton.value = running ? 'Stop' : 'Start';
document.getElementById("outputt").style.backgroundColor = running ? "#2DB37B" : "#B3321B";
if (running) updateTimer(0)
}
function updateTimer(currentTime) {
if (running) requestAnimationFrame(updateTimer)
if (currentTime >= (lastTime + 1000)) {
counter++;
lastTime = currentTime;
let {
hours,
minutes,
seconds
} = calculateTime(counter * 1000);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
}
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 60;
let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
let seconds = Math.floor(milliS / SECONDS) % RESET;
return {
hours,
minutes,
seconds
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
<input id="startstopbutton" class="buttonZ" style="width: 120px;" type="button" name="btn" value="Start" onclick="startstop();">
<input id="resetbutton" class="buttonZ" style="width: 120px;" type="button" name="btnRst" id='btnRst' value="Reset" onclick="resetclock();" />
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>

why my timer in POO doesn't work ? (javascript)

I created a timer in procedural which works ....
```let startMinutes = 1;
let time = startMinutes * 60;
let btnTimer = document.getElementById('btn-signer');
const countdownElt = document.getElementById('countdown');
let interval = setInterval(updateCountDown, 1000);
function startTimer() {
interval = setInterval(updateCountDown, 1000);
}
btnTimer.addEventListener("click", function () {
startTimer()
});
function updateCountDown() {
const minutes = Math.floor(time / 60); //nombre entier
let seconds = time % 60; //reste division
seconds = seconds < 10 ? '0' + seconds : seconds;
countdownElt.innerHTML = `Temps restant : ${minutes}min ${seconds}sec`;
time--;
if (minutes <= 0 && seconds <= 0) {
clearInterval(interval);
countdownElt.innerHTML = "Temps écoulé, veuillez réserver à nouveau.";```
I have to transform it in POO but it doesn't works, I thank that maybe I need to create parameters in consctructor but I don't know if it's really necessary and I don't know which parameters to use..
``` class countDown {
constructor() {
this.btnTimer = document.getElementById('btn-signer');
this.countdownElt = document.getElementById('countdown');
this.startMinutes = 1;
this.timeSec = this.startMinutes * 60;
this.interval = setInterval(this.updateCountDown, 1000);
this.btnTimer.addEventListener('click', () => this.startTimer());
this.updateCountDown();
}
startTimer() {
this.interval = setInterval(this.updateCountDown, 1000);
//console.log(this.interval);
}
updateCountDown() {
let minutes = Math.floor(this.timeSec / 60); //nombre entier
let seconds = this.timeSec % 60; //reste division
seconds = seconds < 10 ? '0' + seconds : seconds;
document.getElementById('countdown').innerHTML = `Temps restant : ${minutes}min ${seconds}sec`;
this.timeSec--;
if (minutes <= 0 && seconds <= 0) {
clearInterval(this.interval);
this.countdownElt.innerHTML = "Temps écoulé, veuillez réserver à nouveau.";
}
}
}```
Thanks.

Why is my StopWatch not working

I have to create a stopwatch that has a start button, a stop button and a pause button. I have so far reached start and stop buttons but the are not working. What is the issue. I have to even go ahead and create a pause button , how to do that. Please help.
var seconds = 0,
minutes = 0,
hours = 0,
timer;
var toStop = false;
var h1 = document.getElementsByTagName('h1')[0];
function start() {
while (!toStop) {
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
if (hours >= 12) {
hours = 0;
toStop = true;
stop();
}
}
}
document.getElementById("h").value = (hours ? (hours > 9 ? hours : "0" + hours) : "00") + ":" +
(minutes ? (minutes > 9 ? minutes : "0" + minutes) : "00") + ":" +
(seconds > 9 ? seconds : "0" + seconds);
myTimer();
}
}
function stop() {
clearTimeout(myTimer());
console.log(timer);
}
function myTimer() {
timer = setInterval(start, 1000);
console.log(timer);
}
function clear() {
document.getElementById("h").value = "00:00:00";
seconds = 0;
minutes = 0;
hours = 0;
}
<h1 id="h"><time>00:00:00</time></h1>
<button onclick="myTimer()">Start</button>
<button onclick="stop()">Stop</button>
<button onclick="clear()">Clear</button>
while (!toStop) {
Get rid of that. The start() func should just calc and print elapsed time. Also, don't call myTimer(); in start(). You already call setInterval() which will call start() every second.
Instead of calculating the elapsed time the way you do, you should create a Date object in myTimer(). Then in start() create another one. To calc elapsed time, subtract one from the other.
And since you call setInterval() you should call clearInterval() to stop.
var startTime = null;
var timer = null;
var totalTime = 0;
function getElapsedTime()
{
var now = new Date();
return (now - startTime) / 1000;
}
function start() {
// Calculate elapsed time since start btn was hit
var elapsedTime = getElapsedTime();
// Add prev times (from pause)
elapsedTime += totalTime;
// elapsedTime is in seconds. Calc, h/m/s
var hours = Math.floor(elapsedTime / (60 * 60));
elapsedTime %= (60 * 60);
var minutes = Math.floor(elapsedTime / 60);
elapsedTime %= 60;
var seconds = Math.floor(elapsedTime);
if (hours > 12) {
stop();
}
// Build string
var timeStr = (hours < 10 ? "0" : "") + hours + ":" +
(minutes < 10 ? "0" : "") + minutes + ":" +
(seconds < 10 ? "0" : "") + seconds;
// Update timer
document.getElementById("h").textContent = timeStr;
document.getElementById("h").value = timeStr;
}
var amPaused = false;
function pause()
{
if (amPaused) {
amPaused = false;
startTime = new Date();
timer = setInterval(start, 1000);
}
else {
amPaused = true;
totalTime += getElapsedTime();
if (timer) {
clearInterval(timer);
timer = null;
}
}
}
function stop() {
if (timer) {
clearInterval(timer);
timer = null;
}
amPaused = false;
document.getElementById("start").disabled = false;
document.getElementById("stop").disabled = true;
document.getElementById("pause").disabled = true;
}
function myTimer() {
stop();
totalTime = 0;
// Store the start time
startTime = new Date();
// Start the timer
timer = setInterval(start, 1000);
document.getElementById("start").disabled = true;
document.getElementById("stop").disabled = false;
document.getElementById("pause").disabled = false;
}
<h1><time id="h">00:00:00</time></h1>
<button onclick="myTimer()" id="start">▶</button>
<button onclick="stop()" id="stop" disabled="true">■</button>
<button onclick="pause()" id="pause" disabled="true">⏸</button>
<html>
<head>
<title>
Stop-Watch
</title>
</head>
<body>
<script>
var myCounter;
var seconds = 0, minutes = 0, hours = 0, timer;
var toStop = false;
var h1 = document.getElementsByTagName('h1')[0];
function start() {
if (!toStop) {
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
if (hours >= 12) {
hours = 0;
toStop = true;
stop();
}
}
}
var time = (hours ? (hours > 9 ? hours : "0" + hours) : "00") + ":" +
(minutes ? (minutes > 9 ? minutes : "0" + minutes) : "00") + ":" +
(seconds > 9 ? seconds : "0" + seconds);
document.getElementById("h").innerHTML = time;
// myTimer();
}
}
function stop() {
toStop = true;
clearInterval(myCounter);
}
function myTimer() {
if (!toStop) {
document.getElementById("h").innerHTML = "00:00:00";
seconds = 0; minutes = 0; hours = 0;
}
clearInterval(myCounter);
toStop = false;
myCounter = setInterval(start, 1000);
}
function clearTime() {
toStop = true;
seconds = 0; minutes = 0; hours = 0;
clearInterval(myCounter);
document.getElementById("h").innerHTML = "00:00:00";
}
</script>
<h1>
<time id="h">00:00:00</time>
</h1>
<button onclick="myTimer()">Start</button>
<button onclick="stop()">Stop</button>
<button onclick="clearTime()">Clear</button>
</body>
</html>
Don't use while or any loops they block io events, so there is no chance for the browser to update html.
If you like to use loops then you could try async/await:
async start() {
while (!stop) {
await sleep(1000)
// some code to get elapsedTime
render()
}
}
function sleep(ms) {
return new Promise(resolve => {
setTimeout(() => resolve(), ms)
})
}
But I like to avoid to use loops for this task.
I believe it could be less coding and cleaner.
const timer = {
start() {
this.time = new Date()
clearInterval(this.interval)
this.interval = setInterval(() => { this.tick() }, 1000)
},
stop() {
clearInterval(this.interval)
},
reset() {
this.time = new Date()
clearInterval(this.interval)
this.render(0, 0, 0)
},
tick() {
const now = new Date()
const elapsedTime = new Date(now - this.time)
const hours = elapsedTime.getUTCHours()
const minutes = elapsedTime.getUTCMinutes()
const seconds = elapsedTime.getUTCSeconds()
if (hours >= 12) this.stop()
this.render(hours, minutes, seconds)
},
render(hours, minutes, seconds) {
const time = [hours, minutes, seconds].map(v => v.toString().padStart(2, `0`))
const html = document.getElementById(`timer`)
html.textContent = time.join(`:`)
}
}
<h1>
<time id="timer">00:00:00</time>
</h1>
<button onclick="timer.start()">Start</button>
<button onclick="timer.stop()">Stop</button>
<button onclick="timer.reset()">Reset</button>

Categories

Resources