Display the time left of a given time
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
2
down vote
favorite
Context
This software is a countdown/visual timer. It was initially developed to display the time left before the end of an exam.
Question
My JavaScript is working but when I want to validate it by some code quality tool it keeps throwing me this error: SyntaxError: Unexpected keyword 'const'. Const declarations are not supported in strict mode.
If any of you can improve this code and help me to improve it(even the design), I would be very grateful.
Here's the code:
'use strict'
const load = () =>
const data =
init(data)
setInterval(() =>
update(data)
render(data)
, 100)
document.querySelector('#start').onclick = event =>
start(data)
document.querySelector('#reset').onclick = event =>
reset(data)
document.querySelector('#license').onclick = showLicense
const init = data =>
data.canvas = document.querySelector('#countdown')
data.ctx = data.canvas.getContext('2d')
data.endTime = Date.now()
data.start = false
data.reset = false
data.canvas.width = data.canvas.height = 500
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
let angle = Math.abs(startAngle)
ctx.save()
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.scale(1, -1)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)'
else
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'
while (angle > 2 * Math.PI)
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI)
ctx.fill()
angle -= 2 * Math.PI
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle)
ctx.lineTo(canvas.width / 2, canvas.height / 2)
ctx.fill()
ctx.restore()
const start = data =>
const button = document.querySelector('#start')
if (data.start)
button.innerText = 'Start'
else
let hours = document.querySelector('#hours').value
let minutes = document.querySelector('#minutes').value
let seconds = document.querySelector('#seconds').value
if (hours.trim() == '') hours = 0
if (minutes.trim() == '') minutes = 0
if (seconds.trim() == '') seconds = 0
let sign
if (hours != 0)
sign = hours < 0 ? -1 : 1
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1
else
sign = seconds < 0 ? -1 : 1
minutes = sign * Math.abs(minutes)
seconds = sign * Math.abs(seconds)
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds
button.innerText = 'Pause'
data.start ^= true
const reset = data =>
data.endTime = Date.now()
data.start = false
document.querySelector('#hours').value = ''
document.querySelector('#minutes').value = ''
document.querySelector('#seconds').value = ''
document.querySelector('#start').innerText = 'Start'
data.reset = true
const showLicense = () =>
const mask = document.createElement('div')
mask.style = 'position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(0, 0, 0, 0.5);z-index:1;'
mask.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
document.body.appendChild(mask)
const message = document.createElement('div')
message.style = 'position:fixed;left:50%;top:50%;transform:translate(-50%, -50%);z-index:2;background:#fff;padding:10px;border-radius:10px;overflow:auto;max-width:600px;max-height:500px;'
message.innerText = 'License unavailable, please report as a bug.'
message.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
const ajax = new XMLHttpRequest()
ajax.open('GET', 'LICENSE')
ajax.onreadystatechange = () =>
console.log('AJAX');
message.innerText = ajax.responseText
ajax.send()
document.body.append(message)
const timeToAngle = endTime =>
const difference = endTime - Date.now()
return difference / 3600000 * 2 * Math.PI
const pad = (value, format) => (format + value).slice(-format.length)
load()
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours, #minutes, #seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Countdown</title>
<script src="countdown.js" defer></script>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
</body>
</html>
javascript html css ecmascript-6 dom
add a comment |Â
up vote
2
down vote
favorite
Context
This software is a countdown/visual timer. It was initially developed to display the time left before the end of an exam.
Question
My JavaScript is working but when I want to validate it by some code quality tool it keeps throwing me this error: SyntaxError: Unexpected keyword 'const'. Const declarations are not supported in strict mode.
If any of you can improve this code and help me to improve it(even the design), I would be very grateful.
Here's the code:
'use strict'
const load = () =>
const data =
init(data)
setInterval(() =>
update(data)
render(data)
, 100)
document.querySelector('#start').onclick = event =>
start(data)
document.querySelector('#reset').onclick = event =>
reset(data)
document.querySelector('#license').onclick = showLicense
const init = data =>
data.canvas = document.querySelector('#countdown')
data.ctx = data.canvas.getContext('2d')
data.endTime = Date.now()
data.start = false
data.reset = false
data.canvas.width = data.canvas.height = 500
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
let angle = Math.abs(startAngle)
ctx.save()
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.scale(1, -1)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)'
else
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'
while (angle > 2 * Math.PI)
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI)
ctx.fill()
angle -= 2 * Math.PI
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle)
ctx.lineTo(canvas.width / 2, canvas.height / 2)
ctx.fill()
ctx.restore()
const start = data =>
const button = document.querySelector('#start')
if (data.start)
button.innerText = 'Start'
else
let hours = document.querySelector('#hours').value
let minutes = document.querySelector('#minutes').value
let seconds = document.querySelector('#seconds').value
if (hours.trim() == '') hours = 0
if (minutes.trim() == '') minutes = 0
if (seconds.trim() == '') seconds = 0
let sign
if (hours != 0)
sign = hours < 0 ? -1 : 1
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1
else
sign = seconds < 0 ? -1 : 1
minutes = sign * Math.abs(minutes)
seconds = sign * Math.abs(seconds)
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds
button.innerText = 'Pause'
data.start ^= true
const reset = data =>
data.endTime = Date.now()
data.start = false
document.querySelector('#hours').value = ''
document.querySelector('#minutes').value = ''
document.querySelector('#seconds').value = ''
document.querySelector('#start').innerText = 'Start'
data.reset = true
const showLicense = () =>
const mask = document.createElement('div')
mask.style = 'position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(0, 0, 0, 0.5);z-index:1;'
mask.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
document.body.appendChild(mask)
const message = document.createElement('div')
message.style = 'position:fixed;left:50%;top:50%;transform:translate(-50%, -50%);z-index:2;background:#fff;padding:10px;border-radius:10px;overflow:auto;max-width:600px;max-height:500px;'
message.innerText = 'License unavailable, please report as a bug.'
message.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
const ajax = new XMLHttpRequest()
ajax.open('GET', 'LICENSE')
ajax.onreadystatechange = () =>
console.log('AJAX');
message.innerText = ajax.responseText
ajax.send()
document.body.append(message)
const timeToAngle = endTime =>
const difference = endTime - Date.now()
return difference / 3600000 * 2 * Math.PI
const pad = (value, format) => (format + value).slice(-format.length)
load()
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours, #minutes, #seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Countdown</title>
<script src="countdown.js" defer></script>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
</body>
</html>
javascript html css ecmascript-6 dom
1
You're using a browser version that doesn't supportconst
when in strict mode, most likely a Safari version prior to 10. Simple fix is to just usevar
or transpile with babel. See this StackOverflow question. I'd say this question is also better placed over at the Stack Overflow site.
â Sven
Mar 22 at 17:59
I've edited the title and I don't know why it can't pass jslint.com or other validators will keep throwing the same error(I'm pretty lost.) @Svenskunganka
â DIDIx13
Mar 22 at 18:33
1
jslint.com does not support latest JavaScript syntax - it's a pretty outdated site. Try ESLint instead.
â Rene Saarsoo
May 5 at 11:39
add a comment |Â
up vote
2
down vote
favorite
up vote
2
down vote
favorite
Context
This software is a countdown/visual timer. It was initially developed to display the time left before the end of an exam.
Question
My JavaScript is working but when I want to validate it by some code quality tool it keeps throwing me this error: SyntaxError: Unexpected keyword 'const'. Const declarations are not supported in strict mode.
If any of you can improve this code and help me to improve it(even the design), I would be very grateful.
Here's the code:
'use strict'
const load = () =>
const data =
init(data)
setInterval(() =>
update(data)
render(data)
, 100)
document.querySelector('#start').onclick = event =>
start(data)
document.querySelector('#reset').onclick = event =>
reset(data)
document.querySelector('#license').onclick = showLicense
const init = data =>
data.canvas = document.querySelector('#countdown')
data.ctx = data.canvas.getContext('2d')
data.endTime = Date.now()
data.start = false
data.reset = false
data.canvas.width = data.canvas.height = 500
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
let angle = Math.abs(startAngle)
ctx.save()
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.scale(1, -1)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)'
else
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'
while (angle > 2 * Math.PI)
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI)
ctx.fill()
angle -= 2 * Math.PI
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle)
ctx.lineTo(canvas.width / 2, canvas.height / 2)
ctx.fill()
ctx.restore()
const start = data =>
const button = document.querySelector('#start')
if (data.start)
button.innerText = 'Start'
else
let hours = document.querySelector('#hours').value
let minutes = document.querySelector('#minutes').value
let seconds = document.querySelector('#seconds').value
if (hours.trim() == '') hours = 0
if (minutes.trim() == '') minutes = 0
if (seconds.trim() == '') seconds = 0
let sign
if (hours != 0)
sign = hours < 0 ? -1 : 1
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1
else
sign = seconds < 0 ? -1 : 1
minutes = sign * Math.abs(minutes)
seconds = sign * Math.abs(seconds)
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds
button.innerText = 'Pause'
data.start ^= true
const reset = data =>
data.endTime = Date.now()
data.start = false
document.querySelector('#hours').value = ''
document.querySelector('#minutes').value = ''
document.querySelector('#seconds').value = ''
document.querySelector('#start').innerText = 'Start'
data.reset = true
const showLicense = () =>
const mask = document.createElement('div')
mask.style = 'position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(0, 0, 0, 0.5);z-index:1;'
mask.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
document.body.appendChild(mask)
const message = document.createElement('div')
message.style = 'position:fixed;left:50%;top:50%;transform:translate(-50%, -50%);z-index:2;background:#fff;padding:10px;border-radius:10px;overflow:auto;max-width:600px;max-height:500px;'
message.innerText = 'License unavailable, please report as a bug.'
message.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
const ajax = new XMLHttpRequest()
ajax.open('GET', 'LICENSE')
ajax.onreadystatechange = () =>
console.log('AJAX');
message.innerText = ajax.responseText
ajax.send()
document.body.append(message)
const timeToAngle = endTime =>
const difference = endTime - Date.now()
return difference / 3600000 * 2 * Math.PI
const pad = (value, format) => (format + value).slice(-format.length)
load()
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours, #minutes, #seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Countdown</title>
<script src="countdown.js" defer></script>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
</body>
</html>
javascript html css ecmascript-6 dom
Context
This software is a countdown/visual timer. It was initially developed to display the time left before the end of an exam.
Question
My JavaScript is working but when I want to validate it by some code quality tool it keeps throwing me this error: SyntaxError: Unexpected keyword 'const'. Const declarations are not supported in strict mode.
If any of you can improve this code and help me to improve it(even the design), I would be very grateful.
Here's the code:
'use strict'
const load = () =>
const data =
init(data)
setInterval(() =>
update(data)
render(data)
, 100)
document.querySelector('#start').onclick = event =>
start(data)
document.querySelector('#reset').onclick = event =>
reset(data)
document.querySelector('#license').onclick = showLicense
const init = data =>
data.canvas = document.querySelector('#countdown')
data.ctx = data.canvas.getContext('2d')
data.endTime = Date.now()
data.start = false
data.reset = false
data.canvas.width = data.canvas.height = 500
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
let angle = Math.abs(startAngle)
ctx.save()
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.scale(1, -1)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)'
else
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'
while (angle > 2 * Math.PI)
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI)
ctx.fill()
angle -= 2 * Math.PI
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle)
ctx.lineTo(canvas.width / 2, canvas.height / 2)
ctx.fill()
ctx.restore()
const start = data =>
const button = document.querySelector('#start')
if (data.start)
button.innerText = 'Start'
else
let hours = document.querySelector('#hours').value
let minutes = document.querySelector('#minutes').value
let seconds = document.querySelector('#seconds').value
if (hours.trim() == '') hours = 0
if (minutes.trim() == '') minutes = 0
if (seconds.trim() == '') seconds = 0
let sign
if (hours != 0)
sign = hours < 0 ? -1 : 1
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1
else
sign = seconds < 0 ? -1 : 1
minutes = sign * Math.abs(minutes)
seconds = sign * Math.abs(seconds)
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds
button.innerText = 'Pause'
data.start ^= true
const reset = data =>
data.endTime = Date.now()
data.start = false
document.querySelector('#hours').value = ''
document.querySelector('#minutes').value = ''
document.querySelector('#seconds').value = ''
document.querySelector('#start').innerText = 'Start'
data.reset = true
const showLicense = () =>
const mask = document.createElement('div')
mask.style = 'position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(0, 0, 0, 0.5);z-index:1;'
mask.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
document.body.appendChild(mask)
const message = document.createElement('div')
message.style = 'position:fixed;left:50%;top:50%;transform:translate(-50%, -50%);z-index:2;background:#fff;padding:10px;border-radius:10px;overflow:auto;max-width:600px;max-height:500px;'
message.innerText = 'License unavailable, please report as a bug.'
message.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
const ajax = new XMLHttpRequest()
ajax.open('GET', 'LICENSE')
ajax.onreadystatechange = () =>
console.log('AJAX');
message.innerText = ajax.responseText
ajax.send()
document.body.append(message)
const timeToAngle = endTime =>
const difference = endTime - Date.now()
return difference / 3600000 * 2 * Math.PI
const pad = (value, format) => (format + value).slice(-format.length)
load()
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours, #minutes, #seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Countdown</title>
<script src="countdown.js" defer></script>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
</body>
</html>
'use strict'
const load = () =>
const data =
init(data)
setInterval(() =>
update(data)
render(data)
, 100)
document.querySelector('#start').onclick = event =>
start(data)
document.querySelector('#reset').onclick = event =>
reset(data)
document.querySelector('#license').onclick = showLicense
const init = data =>
data.canvas = document.querySelector('#countdown')
data.ctx = data.canvas.getContext('2d')
data.endTime = Date.now()
data.start = false
data.reset = false
data.canvas.width = data.canvas.height = 500
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
let angle = Math.abs(startAngle)
ctx.save()
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.scale(1, -1)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)'
else
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'
while (angle > 2 * Math.PI)
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI)
ctx.fill()
angle -= 2 * Math.PI
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle)
ctx.lineTo(canvas.width / 2, canvas.height / 2)
ctx.fill()
ctx.restore()
const start = data =>
const button = document.querySelector('#start')
if (data.start)
button.innerText = 'Start'
else
let hours = document.querySelector('#hours').value
let minutes = document.querySelector('#minutes').value
let seconds = document.querySelector('#seconds').value
if (hours.trim() == '') hours = 0
if (minutes.trim() == '') minutes = 0
if (seconds.trim() == '') seconds = 0
let sign
if (hours != 0)
sign = hours < 0 ? -1 : 1
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1
else
sign = seconds < 0 ? -1 : 1
minutes = sign * Math.abs(minutes)
seconds = sign * Math.abs(seconds)
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds
button.innerText = 'Pause'
data.start ^= true
const reset = data =>
data.endTime = Date.now()
data.start = false
document.querySelector('#hours').value = ''
document.querySelector('#minutes').value = ''
document.querySelector('#seconds').value = ''
document.querySelector('#start').innerText = 'Start'
data.reset = true
const showLicense = () =>
const mask = document.createElement('div')
mask.style = 'position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(0, 0, 0, 0.5);z-index:1;'
mask.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
document.body.appendChild(mask)
const message = document.createElement('div')
message.style = 'position:fixed;left:50%;top:50%;transform:translate(-50%, -50%);z-index:2;background:#fff;padding:10px;border-radius:10px;overflow:auto;max-width:600px;max-height:500px;'
message.innerText = 'License unavailable, please report as a bug.'
message.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
const ajax = new XMLHttpRequest()
ajax.open('GET', 'LICENSE')
ajax.onreadystatechange = () =>
console.log('AJAX');
message.innerText = ajax.responseText
ajax.send()
document.body.append(message)
const timeToAngle = endTime =>
const difference = endTime - Date.now()
return difference / 3600000 * 2 * Math.PI
const pad = (value, format) => (format + value).slice(-format.length)
load()
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours, #minutes, #seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Countdown</title>
<script src="countdown.js" defer></script>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
</body>
</html>
'use strict'
const load = () =>
const data =
init(data)
setInterval(() =>
update(data)
render(data)
, 100)
document.querySelector('#start').onclick = event =>
start(data)
document.querySelector('#reset').onclick = event =>
reset(data)
document.querySelector('#license').onclick = showLicense
const init = data =>
data.canvas = document.querySelector('#countdown')
data.ctx = data.canvas.getContext('2d')
data.endTime = Date.now()
data.start = false
data.reset = false
data.canvas.width = data.canvas.height = 500
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
let angle = Math.abs(startAngle)
ctx.save()
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.scale(1, -1)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)'
else
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(-Math.PI / 2)
ctx.translate(-canvas.width / 2, -canvas.height / 2)
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'
while (angle > 2 * Math.PI)
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI)
ctx.fill()
angle -= 2 * Math.PI
ctx.beginPath()
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle)
ctx.lineTo(canvas.width / 2, canvas.height / 2)
ctx.fill()
ctx.restore()
const start = data =>
const button = document.querySelector('#start')
if (data.start)
button.innerText = 'Start'
else
let hours = document.querySelector('#hours').value
let minutes = document.querySelector('#minutes').value
let seconds = document.querySelector('#seconds').value
if (hours.trim() == '') hours = 0
if (minutes.trim() == '') minutes = 0
if (seconds.trim() == '') seconds = 0
let sign
if (hours != 0)
sign = hours < 0 ? -1 : 1
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1
else
sign = seconds < 0 ? -1 : 1
minutes = sign * Math.abs(minutes)
seconds = sign * Math.abs(seconds)
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds
button.innerText = 'Pause'
data.start ^= true
const reset = data =>
data.endTime = Date.now()
data.start = false
document.querySelector('#hours').value = ''
document.querySelector('#minutes').value = ''
document.querySelector('#seconds').value = ''
document.querySelector('#start').innerText = 'Start'
data.reset = true
const showLicense = () =>
const mask = document.createElement('div')
mask.style = 'position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(0, 0, 0, 0.5);z-index:1;'
mask.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
document.body.appendChild(mask)
const message = document.createElement('div')
message.style = 'position:fixed;left:50%;top:50%;transform:translate(-50%, -50%);z-index:2;background:#fff;padding:10px;border-radius:10px;overflow:auto;max-width:600px;max-height:500px;'
message.innerText = 'License unavailable, please report as a bug.'
message.onclick = () =>
document.body.removeChild(mask)
document.body.removeChild(message)
const ajax = new XMLHttpRequest()
ajax.open('GET', 'LICENSE')
ajax.onreadystatechange = () =>
console.log('AJAX');
message.innerText = ajax.responseText
ajax.send()
document.body.append(message)
const timeToAngle = endTime =>
const difference = endTime - Date.now()
return difference / 3600000 * 2 * Math.PI
const pad = (value, format) => (format + value).slice(-format.length)
load()
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours, #minutes, #seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Countdown</title>
<script src="countdown.js" defer></script>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
</body>
</html>
javascript html css ecmascript-6 dom
edited May 7 at 16:12
Sam Onela
5,82961544
5,82961544
asked Mar 22 at 16:23
DIDIx13
1186
1186
1
You're using a browser version that doesn't supportconst
when in strict mode, most likely a Safari version prior to 10. Simple fix is to just usevar
or transpile with babel. See this StackOverflow question. I'd say this question is also better placed over at the Stack Overflow site.
â Sven
Mar 22 at 17:59
I've edited the title and I don't know why it can't pass jslint.com or other validators will keep throwing the same error(I'm pretty lost.) @Svenskunganka
â DIDIx13
Mar 22 at 18:33
1
jslint.com does not support latest JavaScript syntax - it's a pretty outdated site. Try ESLint instead.
â Rene Saarsoo
May 5 at 11:39
add a comment |Â
1
You're using a browser version that doesn't supportconst
when in strict mode, most likely a Safari version prior to 10. Simple fix is to just usevar
or transpile with babel. See this StackOverflow question. I'd say this question is also better placed over at the Stack Overflow site.
â Sven
Mar 22 at 17:59
I've edited the title and I don't know why it can't pass jslint.com or other validators will keep throwing the same error(I'm pretty lost.) @Svenskunganka
â DIDIx13
Mar 22 at 18:33
1
jslint.com does not support latest JavaScript syntax - it's a pretty outdated site. Try ESLint instead.
â Rene Saarsoo
May 5 at 11:39
1
1
You're using a browser version that doesn't support
const
when in strict mode, most likely a Safari version prior to 10. Simple fix is to just use var
or transpile with babel. See this StackOverflow question. I'd say this question is also better placed over at the Stack Overflow site.â Sven
Mar 22 at 17:59
You're using a browser version that doesn't support
const
when in strict mode, most likely a Safari version prior to 10. Simple fix is to just use var
or transpile with babel. See this StackOverflow question. I'd say this question is also better placed over at the Stack Overflow site.â Sven
Mar 22 at 17:59
I've edited the title and I don't know why it can't pass jslint.com or other validators will keep throwing the same error(I'm pretty lost.) @Svenskunganka
â DIDIx13
Mar 22 at 18:33
I've edited the title and I don't know why it can't pass jslint.com or other validators will keep throwing the same error(I'm pretty lost.) @Svenskunganka
â DIDIx13
Mar 22 at 18:33
1
1
jslint.com does not support latest JavaScript syntax - it's a pretty outdated site. Try ESLint instead.
â Rene Saarsoo
May 5 at 11:39
jslint.com does not support latest JavaScript syntax - it's a pretty outdated site. Try ESLint instead.
â Rene Saarsoo
May 5 at 11:39
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
2
down vote
You're passing your app state as the parameter data
to almost all the functions.
This suggests you're better off combining these functions into a class, where you could assign these values to this
which would be automatically accessible from all methods in class.
Alternatively, if this all is a tiny app, you could just use global variables, which too would be accessible from all functions.
add a comment |Â
up vote
1
down vote
Review points
- Fetching DOM elements is not cheap, so cache those in a variable (perhaps in
data
) instead of accessing them each time. When fetching DOM elements by id attribute, use
document.getElementById()
instead ofdocument.querySelector('#')
refer to answers to this SO question and the relevant jsPerf testAn event delegate can reduce the number of click handlers needed - the example snippet below uses Function.bind() to create partially applied functions for a simple event delegate - if you didn't use an event delegate, the click handlers like:
document.querySelector('#start').onclick = event =>
start(data)Could be simplified like this:
document.querySelector('#start').onclick = start.bind(null, data)
- Unless you fully understand the ways Automatic semicolon insertion can be broken by certain statements, add semi-colons to terminate the lines.
- The AJAX statechange handler (i.e. registered via
onreadystatechange
) doesn't check thereadyState
andstatus
properties of the request, and thus theinnerText
property ofmessage
will likely be updated before desired (e.g. during request open, transmission, loading, etc.)
Rewrite
Below is what one could do with the advice above.
'use strict'
const load = () =>
const data = ;
init(data);
setInterval(() =>
update(data);
render(data);
, 100);
const mapping =
"start": start.bind(null, data),
"reset": reset.bind(null, data),
"license": showLicense
;
const clickHandler = event => event.target.id in mapping && mapping[event.target.id]();
document.body.addEventListener('click', clickHandler);
const init = data =>
data.canvas = document.getElementById('countdown');
data.ctx = data.canvas.getContext('2d');
data.endTime = Date.now();
data.start = false;
data.reset = false;
data.canvas.width = data.canvas.height = 500;
const inputs = ['hours', 'minutes', 'seconds'];
inputs.forEach(inputName => data[inputName + 'Input'] = document.getElementById(inputName));
data.startButton = document.getElementById('start');
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let angle = Math.abs(startAngle);
ctx.save();
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.scale(1, -1);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
else
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
while (angle > 2 * Math.PI)
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI);
ctx.fill();
angle -= 2 * Math.PI;
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
ctx.restore();
const start = data =>
if (data.start)
data.startButton.innerText = 'Start';
else
let hours = data.hoursInput.value;
let minutes = data.minutesInput.value;
let seconds = data.secondsInput.value;
if (hours.trim() == '') hours = 0;
if (minutes.trim() == '') minutes = 0;
if (seconds.trim() == '') seconds = 0;
let sign;
if (hours != 0)
sign = hours < 0 ? -1 : 1;
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1;
else
sign = seconds < 0 ? -1 : 1;
minutes = sign * Math.abs(minutes);
seconds = sign * Math.abs(seconds);
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds;
data.startButton.innerText = 'Pause';
data.start ^= true;
const reset = data =>
data.endTime = Date.now();
data.start = false;
data.hoursInput.value = '';
data.minutesInput.value = '';
data.secondsInput.value = '';
data.startButton.innerText = 'Start';
data.reset = true;
const showLicense = () =>
const mask = document.createElement('div');
mask.id = 'mask';
mask.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
document.body.appendChild(mask);
const message = document.createElement('div');
message.id = 'messageContainer';
message.innerText = 'License unavailable, please report as a bug.';
message.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
const ajax = new XMLHttpRequest();
ajax.open('GET', '/');
ajax.onreadystatechange = () =>
//check state, this doesn't make sense here in this snippet
if (ajax.readyState === 4 && ajax.status === 200)
//message.innerText = ajax.responseText
;
ajax.send();
document.body.append(message);
const timeToAngle = endTime =>
const difference = endTime - Date.now();
return difference / 3600000 * 2 * Math.PI;
const pad = (value, format) => (format + value).slice(-format.length);
load();
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours,
#minutes,
#seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
#mask
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
#messageContainer
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
background: #fff;
padding: 10px;
border-radius: 10px;
overflow: auto;
max-width: 600px;
max-height: 500px;
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
You're passing your app state as the parameter data
to almost all the functions.
This suggests you're better off combining these functions into a class, where you could assign these values to this
which would be automatically accessible from all methods in class.
Alternatively, if this all is a tiny app, you could just use global variables, which too would be accessible from all functions.
add a comment |Â
up vote
2
down vote
You're passing your app state as the parameter data
to almost all the functions.
This suggests you're better off combining these functions into a class, where you could assign these values to this
which would be automatically accessible from all methods in class.
Alternatively, if this all is a tiny app, you could just use global variables, which too would be accessible from all functions.
add a comment |Â
up vote
2
down vote
up vote
2
down vote
You're passing your app state as the parameter data
to almost all the functions.
This suggests you're better off combining these functions into a class, where you could assign these values to this
which would be automatically accessible from all methods in class.
Alternatively, if this all is a tiny app, you could just use global variables, which too would be accessible from all functions.
You're passing your app state as the parameter data
to almost all the functions.
This suggests you're better off combining these functions into a class, where you could assign these values to this
which would be automatically accessible from all methods in class.
Alternatively, if this all is a tiny app, you could just use global variables, which too would be accessible from all functions.
answered May 5 at 11:49
Rene Saarsoo
2,016714
2,016714
add a comment |Â
add a comment |Â
up vote
1
down vote
Review points
- Fetching DOM elements is not cheap, so cache those in a variable (perhaps in
data
) instead of accessing them each time. When fetching DOM elements by id attribute, use
document.getElementById()
instead ofdocument.querySelector('#')
refer to answers to this SO question and the relevant jsPerf testAn event delegate can reduce the number of click handlers needed - the example snippet below uses Function.bind() to create partially applied functions for a simple event delegate - if you didn't use an event delegate, the click handlers like:
document.querySelector('#start').onclick = event =>
start(data)Could be simplified like this:
document.querySelector('#start').onclick = start.bind(null, data)
- Unless you fully understand the ways Automatic semicolon insertion can be broken by certain statements, add semi-colons to terminate the lines.
- The AJAX statechange handler (i.e. registered via
onreadystatechange
) doesn't check thereadyState
andstatus
properties of the request, and thus theinnerText
property ofmessage
will likely be updated before desired (e.g. during request open, transmission, loading, etc.)
Rewrite
Below is what one could do with the advice above.
'use strict'
const load = () =>
const data = ;
init(data);
setInterval(() =>
update(data);
render(data);
, 100);
const mapping =
"start": start.bind(null, data),
"reset": reset.bind(null, data),
"license": showLicense
;
const clickHandler = event => event.target.id in mapping && mapping[event.target.id]();
document.body.addEventListener('click', clickHandler);
const init = data =>
data.canvas = document.getElementById('countdown');
data.ctx = data.canvas.getContext('2d');
data.endTime = Date.now();
data.start = false;
data.reset = false;
data.canvas.width = data.canvas.height = 500;
const inputs = ['hours', 'minutes', 'seconds'];
inputs.forEach(inputName => data[inputName + 'Input'] = document.getElementById(inputName));
data.startButton = document.getElementById('start');
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let angle = Math.abs(startAngle);
ctx.save();
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.scale(1, -1);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
else
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
while (angle > 2 * Math.PI)
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI);
ctx.fill();
angle -= 2 * Math.PI;
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
ctx.restore();
const start = data =>
if (data.start)
data.startButton.innerText = 'Start';
else
let hours = data.hoursInput.value;
let minutes = data.minutesInput.value;
let seconds = data.secondsInput.value;
if (hours.trim() == '') hours = 0;
if (minutes.trim() == '') minutes = 0;
if (seconds.trim() == '') seconds = 0;
let sign;
if (hours != 0)
sign = hours < 0 ? -1 : 1;
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1;
else
sign = seconds < 0 ? -1 : 1;
minutes = sign * Math.abs(minutes);
seconds = sign * Math.abs(seconds);
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds;
data.startButton.innerText = 'Pause';
data.start ^= true;
const reset = data =>
data.endTime = Date.now();
data.start = false;
data.hoursInput.value = '';
data.minutesInput.value = '';
data.secondsInput.value = '';
data.startButton.innerText = 'Start';
data.reset = true;
const showLicense = () =>
const mask = document.createElement('div');
mask.id = 'mask';
mask.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
document.body.appendChild(mask);
const message = document.createElement('div');
message.id = 'messageContainer';
message.innerText = 'License unavailable, please report as a bug.';
message.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
const ajax = new XMLHttpRequest();
ajax.open('GET', '/');
ajax.onreadystatechange = () =>
//check state, this doesn't make sense here in this snippet
if (ajax.readyState === 4 && ajax.status === 200)
//message.innerText = ajax.responseText
;
ajax.send();
document.body.append(message);
const timeToAngle = endTime =>
const difference = endTime - Date.now();
return difference / 3600000 * 2 * Math.PI;
const pad = (value, format) => (format + value).slice(-format.length);
load();
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours,
#minutes,
#seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
#mask
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
#messageContainer
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
background: #fff;
padding: 10px;
border-radius: 10px;
overflow: auto;
max-width: 600px;
max-height: 500px;
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
add a comment |Â
up vote
1
down vote
Review points
- Fetching DOM elements is not cheap, so cache those in a variable (perhaps in
data
) instead of accessing them each time. When fetching DOM elements by id attribute, use
document.getElementById()
instead ofdocument.querySelector('#')
refer to answers to this SO question and the relevant jsPerf testAn event delegate can reduce the number of click handlers needed - the example snippet below uses Function.bind() to create partially applied functions for a simple event delegate - if you didn't use an event delegate, the click handlers like:
document.querySelector('#start').onclick = event =>
start(data)Could be simplified like this:
document.querySelector('#start').onclick = start.bind(null, data)
- Unless you fully understand the ways Automatic semicolon insertion can be broken by certain statements, add semi-colons to terminate the lines.
- The AJAX statechange handler (i.e. registered via
onreadystatechange
) doesn't check thereadyState
andstatus
properties of the request, and thus theinnerText
property ofmessage
will likely be updated before desired (e.g. during request open, transmission, loading, etc.)
Rewrite
Below is what one could do with the advice above.
'use strict'
const load = () =>
const data = ;
init(data);
setInterval(() =>
update(data);
render(data);
, 100);
const mapping =
"start": start.bind(null, data),
"reset": reset.bind(null, data),
"license": showLicense
;
const clickHandler = event => event.target.id in mapping && mapping[event.target.id]();
document.body.addEventListener('click', clickHandler);
const init = data =>
data.canvas = document.getElementById('countdown');
data.ctx = data.canvas.getContext('2d');
data.endTime = Date.now();
data.start = false;
data.reset = false;
data.canvas.width = data.canvas.height = 500;
const inputs = ['hours', 'minutes', 'seconds'];
inputs.forEach(inputName => data[inputName + 'Input'] = document.getElementById(inputName));
data.startButton = document.getElementById('start');
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let angle = Math.abs(startAngle);
ctx.save();
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.scale(1, -1);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
else
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
while (angle > 2 * Math.PI)
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI);
ctx.fill();
angle -= 2 * Math.PI;
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
ctx.restore();
const start = data =>
if (data.start)
data.startButton.innerText = 'Start';
else
let hours = data.hoursInput.value;
let minutes = data.minutesInput.value;
let seconds = data.secondsInput.value;
if (hours.trim() == '') hours = 0;
if (minutes.trim() == '') minutes = 0;
if (seconds.trim() == '') seconds = 0;
let sign;
if (hours != 0)
sign = hours < 0 ? -1 : 1;
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1;
else
sign = seconds < 0 ? -1 : 1;
minutes = sign * Math.abs(minutes);
seconds = sign * Math.abs(seconds);
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds;
data.startButton.innerText = 'Pause';
data.start ^= true;
const reset = data =>
data.endTime = Date.now();
data.start = false;
data.hoursInput.value = '';
data.minutesInput.value = '';
data.secondsInput.value = '';
data.startButton.innerText = 'Start';
data.reset = true;
const showLicense = () =>
const mask = document.createElement('div');
mask.id = 'mask';
mask.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
document.body.appendChild(mask);
const message = document.createElement('div');
message.id = 'messageContainer';
message.innerText = 'License unavailable, please report as a bug.';
message.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
const ajax = new XMLHttpRequest();
ajax.open('GET', '/');
ajax.onreadystatechange = () =>
//check state, this doesn't make sense here in this snippet
if (ajax.readyState === 4 && ajax.status === 200)
//message.innerText = ajax.responseText
;
ajax.send();
document.body.append(message);
const timeToAngle = endTime =>
const difference = endTime - Date.now();
return difference / 3600000 * 2 * Math.PI;
const pad = (value, format) => (format + value).slice(-format.length);
load();
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours,
#minutes,
#seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
#mask
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
#messageContainer
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
background: #fff;
padding: 10px;
border-radius: 10px;
overflow: auto;
max-width: 600px;
max-height: 500px;
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
add a comment |Â
up vote
1
down vote
up vote
1
down vote
Review points
- Fetching DOM elements is not cheap, so cache those in a variable (perhaps in
data
) instead of accessing them each time. When fetching DOM elements by id attribute, use
document.getElementById()
instead ofdocument.querySelector('#')
refer to answers to this SO question and the relevant jsPerf testAn event delegate can reduce the number of click handlers needed - the example snippet below uses Function.bind() to create partially applied functions for a simple event delegate - if you didn't use an event delegate, the click handlers like:
document.querySelector('#start').onclick = event =>
start(data)Could be simplified like this:
document.querySelector('#start').onclick = start.bind(null, data)
- Unless you fully understand the ways Automatic semicolon insertion can be broken by certain statements, add semi-colons to terminate the lines.
- The AJAX statechange handler (i.e. registered via
onreadystatechange
) doesn't check thereadyState
andstatus
properties of the request, and thus theinnerText
property ofmessage
will likely be updated before desired (e.g. during request open, transmission, loading, etc.)
Rewrite
Below is what one could do with the advice above.
'use strict'
const load = () =>
const data = ;
init(data);
setInterval(() =>
update(data);
render(data);
, 100);
const mapping =
"start": start.bind(null, data),
"reset": reset.bind(null, data),
"license": showLicense
;
const clickHandler = event => event.target.id in mapping && mapping[event.target.id]();
document.body.addEventListener('click', clickHandler);
const init = data =>
data.canvas = document.getElementById('countdown');
data.ctx = data.canvas.getContext('2d');
data.endTime = Date.now();
data.start = false;
data.reset = false;
data.canvas.width = data.canvas.height = 500;
const inputs = ['hours', 'minutes', 'seconds'];
inputs.forEach(inputName => data[inputName + 'Input'] = document.getElementById(inputName));
data.startButton = document.getElementById('start');
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let angle = Math.abs(startAngle);
ctx.save();
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.scale(1, -1);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
else
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
while (angle > 2 * Math.PI)
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI);
ctx.fill();
angle -= 2 * Math.PI;
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
ctx.restore();
const start = data =>
if (data.start)
data.startButton.innerText = 'Start';
else
let hours = data.hoursInput.value;
let minutes = data.minutesInput.value;
let seconds = data.secondsInput.value;
if (hours.trim() == '') hours = 0;
if (minutes.trim() == '') minutes = 0;
if (seconds.trim() == '') seconds = 0;
let sign;
if (hours != 0)
sign = hours < 0 ? -1 : 1;
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1;
else
sign = seconds < 0 ? -1 : 1;
minutes = sign * Math.abs(minutes);
seconds = sign * Math.abs(seconds);
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds;
data.startButton.innerText = 'Pause';
data.start ^= true;
const reset = data =>
data.endTime = Date.now();
data.start = false;
data.hoursInput.value = '';
data.minutesInput.value = '';
data.secondsInput.value = '';
data.startButton.innerText = 'Start';
data.reset = true;
const showLicense = () =>
const mask = document.createElement('div');
mask.id = 'mask';
mask.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
document.body.appendChild(mask);
const message = document.createElement('div');
message.id = 'messageContainer';
message.innerText = 'License unavailable, please report as a bug.';
message.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
const ajax = new XMLHttpRequest();
ajax.open('GET', '/');
ajax.onreadystatechange = () =>
//check state, this doesn't make sense here in this snippet
if (ajax.readyState === 4 && ajax.status === 200)
//message.innerText = ajax.responseText
;
ajax.send();
document.body.append(message);
const timeToAngle = endTime =>
const difference = endTime - Date.now();
return difference / 3600000 * 2 * Math.PI;
const pad = (value, format) => (format + value).slice(-format.length);
load();
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours,
#minutes,
#seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
#mask
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
#messageContainer
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
background: #fff;
padding: 10px;
border-radius: 10px;
overflow: auto;
max-width: 600px;
max-height: 500px;
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
Review points
- Fetching DOM elements is not cheap, so cache those in a variable (perhaps in
data
) instead of accessing them each time. When fetching DOM elements by id attribute, use
document.getElementById()
instead ofdocument.querySelector('#')
refer to answers to this SO question and the relevant jsPerf testAn event delegate can reduce the number of click handlers needed - the example snippet below uses Function.bind() to create partially applied functions for a simple event delegate - if you didn't use an event delegate, the click handlers like:
document.querySelector('#start').onclick = event =>
start(data)Could be simplified like this:
document.querySelector('#start').onclick = start.bind(null, data)
- Unless you fully understand the ways Automatic semicolon insertion can be broken by certain statements, add semi-colons to terminate the lines.
- The AJAX statechange handler (i.e. registered via
onreadystatechange
) doesn't check thereadyState
andstatus
properties of the request, and thus theinnerText
property ofmessage
will likely be updated before desired (e.g. during request open, transmission, loading, etc.)
Rewrite
Below is what one could do with the advice above.
'use strict'
const load = () =>
const data = ;
init(data);
setInterval(() =>
update(data);
render(data);
, 100);
const mapping =
"start": start.bind(null, data),
"reset": reset.bind(null, data),
"license": showLicense
;
const clickHandler = event => event.target.id in mapping && mapping[event.target.id]();
document.body.addEventListener('click', clickHandler);
const init = data =>
data.canvas = document.getElementById('countdown');
data.ctx = data.canvas.getContext('2d');
data.endTime = Date.now();
data.start = false;
data.reset = false;
data.canvas.width = data.canvas.height = 500;
const inputs = ['hours', 'minutes', 'seconds'];
inputs.forEach(inputName => data[inputName + 'Input'] = document.getElementById(inputName));
data.startButton = document.getElementById('start');
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let angle = Math.abs(startAngle);
ctx.save();
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.scale(1, -1);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
else
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
while (angle > 2 * Math.PI)
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI);
ctx.fill();
angle -= 2 * Math.PI;
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
ctx.restore();
const start = data =>
if (data.start)
data.startButton.innerText = 'Start';
else
let hours = data.hoursInput.value;
let minutes = data.minutesInput.value;
let seconds = data.secondsInput.value;
if (hours.trim() == '') hours = 0;
if (minutes.trim() == '') minutes = 0;
if (seconds.trim() == '') seconds = 0;
let sign;
if (hours != 0)
sign = hours < 0 ? -1 : 1;
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1;
else
sign = seconds < 0 ? -1 : 1;
minutes = sign * Math.abs(minutes);
seconds = sign * Math.abs(seconds);
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds;
data.startButton.innerText = 'Pause';
data.start ^= true;
const reset = data =>
data.endTime = Date.now();
data.start = false;
data.hoursInput.value = '';
data.minutesInput.value = '';
data.secondsInput.value = '';
data.startButton.innerText = 'Start';
data.reset = true;
const showLicense = () =>
const mask = document.createElement('div');
mask.id = 'mask';
mask.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
document.body.appendChild(mask);
const message = document.createElement('div');
message.id = 'messageContainer';
message.innerText = 'License unavailable, please report as a bug.';
message.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
const ajax = new XMLHttpRequest();
ajax.open('GET', '/');
ajax.onreadystatechange = () =>
//check state, this doesn't make sense here in this snippet
if (ajax.readyState === 4 && ajax.status === 200)
//message.innerText = ajax.responseText
;
ajax.send();
document.body.append(message);
const timeToAngle = endTime =>
const difference = endTime - Date.now();
return difference / 3600000 * 2 * Math.PI;
const pad = (value, format) => (format + value).slice(-format.length);
load();
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours,
#minutes,
#seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
#mask
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
#messageContainer
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
background: #fff;
padding: 10px;
border-radius: 10px;
overflow: auto;
max-width: 600px;
max-height: 500px;
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
'use strict'
const load = () =>
const data = ;
init(data);
setInterval(() =>
update(data);
render(data);
, 100);
const mapping =
"start": start.bind(null, data),
"reset": reset.bind(null, data),
"license": showLicense
;
const clickHandler = event => event.target.id in mapping && mapping[event.target.id]();
document.body.addEventListener('click', clickHandler);
const init = data =>
data.canvas = document.getElementById('countdown');
data.ctx = data.canvas.getContext('2d');
data.endTime = Date.now();
data.start = false;
data.reset = false;
data.canvas.width = data.canvas.height = 500;
const inputs = ['hours', 'minutes', 'seconds'];
inputs.forEach(inputName => data[inputName + 'Input'] = document.getElementById(inputName));
data.startButton = document.getElementById('start');
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let angle = Math.abs(startAngle);
ctx.save();
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.scale(1, -1);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
else
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
while (angle > 2 * Math.PI)
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI);
ctx.fill();
angle -= 2 * Math.PI;
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
ctx.restore();
const start = data =>
if (data.start)
data.startButton.innerText = 'Start';
else
let hours = data.hoursInput.value;
let minutes = data.minutesInput.value;
let seconds = data.secondsInput.value;
if (hours.trim() == '') hours = 0;
if (minutes.trim() == '') minutes = 0;
if (seconds.trim() == '') seconds = 0;
let sign;
if (hours != 0)
sign = hours < 0 ? -1 : 1;
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1;
else
sign = seconds < 0 ? -1 : 1;
minutes = sign * Math.abs(minutes);
seconds = sign * Math.abs(seconds);
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds;
data.startButton.innerText = 'Pause';
data.start ^= true;
const reset = data =>
data.endTime = Date.now();
data.start = false;
data.hoursInput.value = '';
data.minutesInput.value = '';
data.secondsInput.value = '';
data.startButton.innerText = 'Start';
data.reset = true;
const showLicense = () =>
const mask = document.createElement('div');
mask.id = 'mask';
mask.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
document.body.appendChild(mask);
const message = document.createElement('div');
message.id = 'messageContainer';
message.innerText = 'License unavailable, please report as a bug.';
message.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
const ajax = new XMLHttpRequest();
ajax.open('GET', '/');
ajax.onreadystatechange = () =>
//check state, this doesn't make sense here in this snippet
if (ajax.readyState === 4 && ajax.status === 200)
//message.innerText = ajax.responseText
;
ajax.send();
document.body.append(message);
const timeToAngle = endTime =>
const difference = endTime - Date.now();
return difference / 3600000 * 2 * Math.PI;
const pad = (value, format) => (format + value).slice(-format.length);
load();
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours,
#minutes,
#seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
#mask
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
#messageContainer
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
background: #fff;
padding: 10px;
border-radius: 10px;
overflow: auto;
max-width: 600px;
max-height: 500px;
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
'use strict'
const load = () =>
const data = ;
init(data);
setInterval(() =>
update(data);
render(data);
, 100);
const mapping =
"start": start.bind(null, data),
"reset": reset.bind(null, data),
"license": showLicense
;
const clickHandler = event => event.target.id in mapping && mapping[event.target.id]();
document.body.addEventListener('click', clickHandler);
const init = data =>
data.canvas = document.getElementById('countdown');
data.ctx = data.canvas.getContext('2d');
data.endTime = Date.now();
data.start = false;
data.reset = false;
data.canvas.width = data.canvas.height = 500;
const inputs = ['hours', 'minutes', 'seconds'];
inputs.forEach(inputName => data[inputName + 'Input'] = document.getElementById(inputName));
data.startButton = document.getElementById('start');
const update = data =>
const render = data =>
const
canvas,
ctx,
startAngle,
start
= data;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let angle = Math.abs(startAngle);
ctx.save();
if (startAngle > 0)
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.scale(1, -1);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';
else
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
while (angle > 2 * Math.PI)
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, 2 * Math.PI);
ctx.fill();
angle -= 2 * Math.PI;
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.width / 2, canvas.width / 2, 0, angle);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
ctx.restore();
const start = data =>
if (data.start)
data.startButton.innerText = 'Start';
else
let hours = data.hoursInput.value;
let minutes = data.minutesInput.value;
let seconds = data.secondsInput.value;
if (hours.trim() == '') hours = 0;
if (minutes.trim() == '') minutes = 0;
if (seconds.trim() == '') seconds = 0;
let sign;
if (hours != 0)
sign = hours < 0 ? -1 : 1;
else if (minutes != 0)
sign = minutes < 0 ? -1 : 1;
else
sign = seconds < 0 ? -1 : 1;
minutes = sign * Math.abs(minutes);
seconds = sign * Math.abs(seconds);
data.endTime = Date.now() + 3600000 * hours + 60000 * minutes + 1000 * seconds;
data.startButton.innerText = 'Pause';
data.start ^= true;
const reset = data =>
data.endTime = Date.now();
data.start = false;
data.hoursInput.value = '';
data.minutesInput.value = '';
data.secondsInput.value = '';
data.startButton.innerText = 'Start';
data.reset = true;
const showLicense = () =>
const mask = document.createElement('div');
mask.id = 'mask';
mask.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
document.body.appendChild(mask);
const message = document.createElement('div');
message.id = 'messageContainer';
message.innerText = 'License unavailable, please report as a bug.';
message.onclick = () =>
document.body.removeChild(mask);
document.body.removeChild(message);
const ajax = new XMLHttpRequest();
ajax.open('GET', '/');
ajax.onreadystatechange = () =>
//check state, this doesn't make sense here in this snippet
if (ajax.readyState === 4 && ajax.status === 200)
//message.innerText = ajax.responseText
;
ajax.send();
document.body.append(message);
const timeToAngle = endTime =>
const difference = endTime - Date.now();
return difference / 3600000 * 2 * Math.PI;
const pad = (value, format) => (format + value).slice(-format.length);
load();
*
box-sizing: border-box;
body
background: #eee;
margin: 0;
main
background: #fff;
max-width: 700px;
margin: auto;
padding: 10px;
display: flex;
flex-flow: column;
align-items: center;
h1
text-align: center;
menu
display: flex;
justify-content: space-between;
width: 500px;
padding: 0;
#hours,
#minutes,
#seconds
width: 30px;
text-align: center;
footer
max-width: 700px;
margin: auto;
background: #ddd;
padding: 10px;
display: flex;
justify-content: space-between;
footer a
display: inline-block;
background: #ccc;
padding: 10px;
border-radius: 10px;
color: black;
text-decoration: none;
cursor: pointer;
footer a:hover
background: #888;
color: #fff;
#mask
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
#messageContainer
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
background: #fff;
padding: 10px;
border-radius: 10px;
overflow: auto;
max-width: 600px;
max-height: 500px;
<main>
<h1 contenteditable>Countdown</h1>
<canvas id="countdown"></canvas>
<menu>
<button id="reset">Reset</button>
<div class="group">
<input id="hours" placeholder="HH" autofocus>:<input id="minutes" placeholder="MM">:<input id="seconds" placeholder="SS">
</div>
<button id="start">Start</button>
</menu>
</main>
<footer>
<a href="https://github.com/SteeveDroz/countdown">Find the project on GitHub</a>
<a href="https://github.com/SteeveDroz/countdown/issues">Report bugs</a>
<a id="license">MIT license</a>
</footer>
edited May 7 at 17:10
answered May 7 at 16:13
Sam Onela
5,82961544
5,82961544
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f190220%2fdisplay-the-time-left-of-a-given-time%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
1
You're using a browser version that doesn't support
const
when in strict mode, most likely a Safari version prior to 10. Simple fix is to just usevar
or transpile with babel. See this StackOverflow question. I'd say this question is also better placed over at the Stack Overflow site.â Sven
Mar 22 at 17:59
I've edited the title and I don't know why it can't pass jslint.com or other validators will keep throwing the same error(I'm pretty lost.) @Svenskunganka
â DIDIx13
Mar 22 at 18:33
1
jslint.com does not support latest JavaScript syntax - it's a pretty outdated site. Try ESLint instead.
â Rene Saarsoo
May 5 at 11:39