Viewing:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Timer</title>
<meta charset="utf-8" />
<meta name="description" content="Minute precision timer.">
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<audio src="432.mp3" id="beep" autostart="false"></audio>
<style>
:root {
color-scheme: light dark;
/* DARK THEME */
--dark-main-bg-color: 0, 0, 0;
--dark-text-color: 240, 240, 240;
/* LIGHT THEME */
--light-main-bg-color: 255, 255, 255;
--light-text-color: 0, 0, 0;
/* CHOOSE DARK THEME */
--main-bg-color: rgb(var(--dark-main-bg-color));
--text-color: rgb(var(--dark-text-color));
}
/* CHOOSE LIGHT THEME */
@media (prefers-color-scheme: light) {
:root {
--main-bg-color: rgb(var(--light-main-bg-color));
--text-color: rgb(var(--light-text-color));
}
}
body {
background: var(--main-bg-color);
color: var(--text-color);
}
</style>
<div style='display: flex; align-items: center;'>
<button autofocus id="toggle" style='min-width: 50px'>Start</button>
<input type="range" min="1" max="120" value="20" id="range">
<label for="range"><span id='numMinutes'>20</span> minutes</label>
</div>
<div id='remaining' style='visibility:hidden;'>Time Left: <span id='minutesLeft'>20</span>:<span id='secondsLeft'>00</span></div>
<script>
(function() {
var getEl = document.getElementById.bind(document);
var DOM = {
// audio element
beep: getEl('beep'),
// top UI
toggle: getEl('toggle'),
range: getEl('range'),
numMinutes: getEl('numMinutes'),
// Time remaining UI
remaining: getEl('remaining'),
minutesLeft: getEl('minutesLeft'),
secondsLeft: getEl('secondsLeft'),
}
var state = {
remaining: 0,
counting: false,
interval: 0,
}
var updateInterval = 5 // in seconds
function updateRemaining(secondsRemaining) {
state.remaining = secondsRemaining
DOM.minutesLeft.innerHTML = Math.floor(secondsRemaining / 60)
DOM.secondsLeft.innerHTML = (secondsRemaining % 60).toString().padStart(2, '0')
}
function start() {
if (state.interval > 0) {
clearInterval(state.interval)
state.interval = 0
}
updateRemaining(DOM.range.value * 60)
DOM.remaining.style.visibility = 'visible'
DOM.toggle.innerHTML = 'Stop'
state.counting = true
DOM.range.disabled = true
state.interval = setInterval(function () {
updateRemaining(state.remaining - updateInterval)
if (state.remaining <= 0) {
DOM.beep.play()
stop()
}
}, updateInterval * 1000)
}
function stop() {
if (state.interval > 0) {
clearInterval(state.interval)
state.interval = 0
}
state.counting = false
DOM.remaining.style.visibility = 'hidden'
DOM.toggle.innerHTML = 'Start'
DOM.range.disabled = false
}
DOM.toggle.addEventListener('click', () => state.counting ? stop() : start())
DOM.range.addEventListener('change', (e) => {
// if (state.counting) {
// event.stopPropagation()
// return false
// }
DOM.numMinutes.innerHTML = e.target.value
})
})()
</script>
</body>
</html>