Instead of calling myDate.setHours(0, 0, 0, 0)
, you should call let myDate = new Date(0)
; but what you really need to do is subtract the current time from the time when the timer started.
If you want to make this reusable, wrap it in a class. Also, you need to take into account the stoppage time when pausing.
Here is an example of decoupling the time, task, and UI. Each stopwatch is independent of one another.
Update: Added some more pizazz with some themes.
/**
* The Stopwatch class is based on:
* https://olinations.medium.com/an-accurate-vanilla-js-stopwatch-script-56ceb5c6f45b
*/
class Stopwatch {
constructor(task, updateRate = 10) {
this.startTime = 0;
this.updatedTime = 0;
this.difference = 0;
this.savedTime = 0;
this.paused = false;
this.running = false;
this.tInterval = null;
this.task = task;
this.updateRate = updateRate;
}
update() {
this.updatedTime = new Date().getTime();
if (this.savedTime) {
this.difference = (this.updatedTime - this.startTime) + this.savedTime;
} else {
this.difference = this.updatedTime - this.startTime;
}
this.task(this.difference);
}
start() {
if (!this.running) {
this.startTime = new Date().getTime();
this.tInterval = setInterval(() => this.update(), this.updateRate);
this.paused = false;
this.running = true;
}
}
pause() {
if (!this.difference) {
// If timer never started, disallow pause button
} else if (!this.paused) {
clearInterval(this.tInterval);
this.savedTime = this.difference;
this.paused = true;
this.running = false;
} else {
// this.start(); // Optional if Start/Pause is toggleable
}
}
reset() {
clearInterval(this.tInterval);
this.tInterval = null;
this.savedTime = 0;
this.difference = 0;
this.paused = false;
this.running = false;
this.task(this.difference);
}
}
const formatTime = (ms) => {
let
hrs = Math.floor(ms / 36e5),
min = Math.floor((ms - (hrs * 36e5)) / 6e4),
sec = Math.floor((ms - (hrs * 36e5) - (min * 6e4)) / 1e3),
mil = Math.floor((ms - (hrs * 36e5) - (min * 6e4) - (sec * 1e3)));
hrs = `${hrs}`.padStart(2, '0');
min = `${min}`.padStart(2, '0');
sec = `${sec}`.padStart(2, '0');
mil = `${mil}`.padStart(3, '0');
return `${hrs}:${min}:${sec}.${mil}`;
}
// Link an internal stopwatch to a UI component.
const LinkStopwatchUI = (stopwatchEl) => {
const
displayEl = stopwatchEl.querySelector('.display'),
startBtn = stopwatchEl.querySelector('.start-btn'),
pauseBtn = stopwatchEl.querySelector('.pause-btn'),
resetBtn = stopwatchEl.querySelector('.reset-btn');
const task = timeDiff => { displayEl.innerHTML = formatTime(timeDiff) };
const stopwatch = new Stopwatch(task);
stopwatch.reset();
startBtn.addEventListener('click', () => stopwatch.start());
pauseBtn.addEventListener('click', () => stopwatch.pause());
resetBtn.addEventListener('click', () => stopwatch.reset());
}
document.querySelectorAll('.stopwatch').forEach(LinkStopwatchUI);
:root {
--sw-bg: #444;
--sw-fg: #0F7;
--sw-bc: #666;
--sw-display-bg: #222;
--sw-display-bc: #000;
--sw-btn-bg: #555;
--sw-btn-fg: #EEE;
--sw-btn-bc: #666;
--sw-btn-hover-bg: #777;
--sw-btn-hover-fg: #FFF;
}
.stopwatch[data-theme="light"] {
--sw-bg: #EEE;
--sw-fg: #06D;
--sw-bc: #666;
--sw-display-bg: #CCC;
--sw-display-bc: #AAA;
--sw-btn-bg: #E7E7E7;
--sw-btn-fg: #111;
--sw-btn-bc: #AAA;
--sw-btn-hover-bg: #FFF;
--sw-btn-hover-fg: #000;
}
/* https://www.color-hex.com/color-palette/52138 */
.stopwatch[data-theme="calm"] {
--sw-bg: #F5F0EC;
--sw-fg: #152C43;
--sw-bc: #72808E;
--sw-display-bg: #BDDAC8;
--sw-display-bc: #DED0C2;
--sw-btn-bg: #DED0C2;
--sw-btn-fg: #152C43;
--sw-btn-bc: #72808E;
--sw-btn-hover-bg: #BDDAC8;
--sw-btn-hover-fg: #152C43;
}
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
background: #111;
}
.stopwatch {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-row-gap: 0.5em;
grid-column-gap: 0.33em;
padding: 0.25em;
color: var(--sw-fg);
background: var(--sw-bg);
border: thin solid var(--sw-bc);
}
.stopwatch button {
background: var(--sw-btn-bg);
color: var(--sw-btn-fg);
border: thin solid var(--sw-btn-bc);
}
.stopwatch button:hover {
background: var(--sw-btn-hover-bg);
color: var(--sw-btn-hover-fg);
cursor: pointer;
}
.stopwatch .display {
grid-column: 1 / 4;
grid-row: 1;
text-align: right;
background: var(--sw-display-bg);
border: thin solid var(--sw-display-bc);
font-family: monospace;
font-size: 1.667em;
padding: 0.125em;
}
.stopwatch .start-btn {
grid-column: 1;
grid-row: 2;
}
.stopwatch .stop-btn {
grid-column: 2;
grid-row: 2;
}
.stopwatch .reset-btn {
grid-column: 3;
grid-row: 2;
}
<div class="stopwatch">
<div class="display"></div>
<button class="start-btn">Start</button>
<button class="pause-btn">Pause</button>
<button class="reset-btn">Reset</button>
</div>
<div class="stopwatch" data-theme="light">
<div class="display"></div>
<button class="start-btn">Start</button>
<button class="pause-btn">Pause</button>
<button class="reset-btn">Reset</button>
</div>
<div class="stopwatch" data-theme="calm">
<div class="display"></div>
<button class="start-btn">Start</button>
<button class="pause-btn">Pause</button>
<button class="reset-btn">Reset</button>
</div>