Stopwatch
Simple stopwatch that can start delayed, in order to time something in other browser tabs/windows
by Larph
HTML
<div id="stopwatch">
<div id="hrs" class="sw-component"></div>:<div id="min" class="sw-component"></div>:<div id="sec" class="sw-component"></div>.<div id="mil" class="sw-component"></div>
</div>
<ul id="buttons">
<li><button onclick="startTimer()">Start</button></li>
<li><button onclick="stopTimer()">Stop</button></li>
<li><button onclick="resetTimer()">Reset</button></li>
<li><label for="delayed" title="Start timer delayed, i.e. when you click Start, the timer won't actually start until you alt-tab/activate another window or switch to another tab on your browser or minimize this browser window.
This is, so you can go to another window and quickly activate something that needs to be timed!">Delayed<input type="checkbox" id="delayed" /></label></li>
</ul>
CSS
body
{
background: #202020;
color: white;
margin: 0;
padding: 0;
}
button
{
width: 100px;
height: 30px;
border-width: 1px;
border-style: solid;
border-color: #fff #333 #333 #fff;
border-radius: 50px;
background: #444;
color: white;
cursor: pointer;
outline: none;
}
button:active
{
background: #333;
border-color: #333 #fff #fff #333;
}
.sw-component
{
display:inline-block;
font-family:Lucida Console;
}
#stopwatch
{
font-size: 34pt;
position: absolute;
top: 40%; left: 50%;
transform: translate(-50%, -55%);
}
#buttons
{
position: absolute;
top: 55%;
left: 30%;
transform: translate(-23%, -45%);
}
#buttons li
{
display: inline;
padding-left: 10px;
}
JavaScript
const HRS = document.getElementById('hrs'), MIN = document.getElementById('min'), SEC = document.getElementById('sec'), MIL = document.getElementById('mil'), DEL = document.getElementById('delayed');
var hrs = min = sec = mil = dt = ps = pt = tt = t = 0, running = started = false, iID;
function timerCycle()
{
if (running)
{
t = performance.now() * 0.001;
dt += t - pt;
pt = t;
tt = Math.floor(dt);
mil = dt - tt;
MIL.textContent = (mil).toFixed(3).slice(-3);
sec = tt%60;
if ( sec == ps ) return;
ps = sec;
min = Math.floor(tt/60)%60;
hrs = Math.floor(tt/3600);
HRS.textContent = ('0'+hrs).slice(-2);
MIN.textContent = ('0'+min).slice(-2);
SEC.textContent = ('0'+sec).slice(-2);
}
}
function stop()
{
if ( iID )
{
clearInterval(iID);
iID = 0;
}
}
function start()
{
if ( started && !running )
{
running = true;
t = pt = performance.now() * 0.001;
iID = setInterval(timerCycle, 33);
}
}
function startTimer()
{
if ( !started )
{
started = true;
stop();
if ( !DEL.checked )
{
start();
}
}
}
function stopTimer()
{
if ( started )
{
started = running = false;
stop();
}
}
function resetTimer()
{
if ( !started )
{
running = false;
dt = ps = 0;
HRS.textContent = '00';
MIN.textContent = '00';
SEC.textContent = '00';
MIL.textContent = '000';
}
}
function onVisibilityChange()
{
if ( started )
{
start();
}
}
resetTimer();
document.addEventListener('visibilitychange', onVisibilityChange);
window.addEventListener('blur', onVisibilityChange);