Wesbos - 타이머


완성본 예시

이전에 노마드코더의 클론코딩 강의에서 시도했다가 실패했던 것과 비슷한 챕터다…
타이머 기능이라고 하면 굉장히 쉬워보이지만…. 막상 해보면 굉장히 복잡하다.

로직

  1. const와 addEventListener
  2. 초(seconds)입력하면 → 분 : 초 형식으로 화면에 띄우기
  3. input 박스 입력된 값을 함수에 전달!
  4. 띄워진 시간 countdown
  5. 타이머 종료 시간(endTime) 계산 → 띄우기

const와 addEventListener

1
2
3
4
5
6
7
8
9
const time = document.querySelector('.display__time-left');
const endTime = document.querySelector('.display__end-time');
const buttons = document.querySelectorAll('[data-time]');

function startTimer() {
// console.log(this.dataset.time);
}

buttons.forEach((button) => button.addEventListener('click', startTimer));

Seconds입력 → Minutes : Seconds 형식으로 반환

💡 분(Minutes) : 초 / 60
초 (Seconds) : 초 % 60

1
2
3
4
5
function displayTimer(seconds) {
const minutes = Math.floor(seconds / 60);
const secondsLeft = seconds % 60;
time.innerHTML = `${minutes}:${secondsLeft}`;
}

But, 이대로 실행할 경우 아래와 같은 문제가 발생한다

0이 안붙는다..

1
2
3
4
5
6
7
8
9
// 조건을 걸어서 10 이하일 경우 앞에 0이 붙어서 출력되도록 해야한다.
function displayTimer(seconds) {
const minutes = Math.floor(seconds / 60);
const secondsLeft = seconds % 60;
// 초가 10보다 작을 경우, 0 붙임
const display = `${minutes}:${secondsLeft < 10 ? '0' : ''}${secondsLeft}`;
document.title = display; // 이건 그냥 타이틀에도 시간 표시되면 좋을 것 같아서 추가
time.innerHTML = display;
}

문제 해결

지금 다시 코드를 봐보니까.. padendpadstart를 사용했어도 됐을 것 같다.


input박스에 입력한 값을 각 함수들에 전달하기

💡 submit 이벤트에 반응할 엘리먼트 및 함수를 만들고, 해당 함수에서 timer에 분값*60을 전송

timer(seconds) 에서 displayTimer() 함수 실행하면 된다.

1
2
3
4
5
6
7
8
9
10
11
function timer(seconds) {
displayTimer(seconds);
}

function submitSeconds(e) {
e.preventDefault();
const inputMins = this.minutes.value;
timer(inputMins * 60);
}

document.customForm.addEventListener('submit', submitSeconds);

띄워진 시간을 카운트다운!

💡 가장 핵심적인 부분이다.

setInterval을 활용해 1초마다 seconds가 —1 되도록 하고
→ seconds가 00을 지나 마이너스 되면
→ minutes가 —1 되도록 구현하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function timer(seconds) {
const now = Date.now(); //현재 시간(날짜) 정보
const then = now + seconds * 1000; // then = 현재 시간 및 날짜정보 + 입력 seconds*1000
console.log(now, then);

displayTimer(seconds);

countdown = setInterval(() => {
// ((현재 시간 + 추가된 시간) - 현재 시간) / 1000
// then은 고정값이고 Date.now는 계속 증가 => 즉, 값이 점점 마이너스 됨
const countSeconds = Math.round((then - Date.now()) / 1000);
if (countSeconds < 0) {
clearInterval(countdown);
return;
}
displayTimer(countSeconds);
}, 1000);
}

부가설명

예시

즉, 고정된 시간 값 (then)에서 계속 증가하는 값을 빼주면 결국 countSeconds는 1초씩 줄어들게 된다.

종료시간 표시하기

💡 위의 내용들을 잘 수행했다면 어렵지 않다.

1
2
3
4
5
6
7
8
function displayEndTime(timestamp) {
const end = new Date(timestamp);
const hours = end.getHours();
const minutes = end.getMinutes();
endTime.textContent = `종료 시간 ${
hours < 10 ? '0' : ''
}${hours} : ${minutes}`;
}

최종 완성 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
let countdown;
let working = false;
const time = document.querySelector('.display__time-left');
const endTime = document.querySelector('.display__end-time');
const buttons = document.querySelectorAll('[data-time]');
const quit = document.querySelector('.quit');

function timer(seconds) {
working = true;
if (working == false) {
return;
}
clearInterval(countdown);
quit.style.setProperty('opacity', '100');
const now = Date.now();
const then = now + seconds * 1000;
displayTimer(seconds);
displayEndTime(then);

countdown = setInterval(() => {
const countSeconds = Math.round((then - Date.now()) / 1000);
if (countSeconds < 0) {
clearInterval(countdown);
return;
}
displayTimer(countSeconds);
}, 1000);
}

function displayTimer(seconds) {
const minutes = Math.floor(seconds / 60);
const secondsLeft = seconds % 60;
const display = `${minutes}:${secondsLeft < 10 ? '0' : ''}${secondsLeft}`;
document.title = display;
time.innerHTML = display;
}

function displayEndTime(timestamp) {
const end = new Date(timestamp);
const hours = end.getHours();
const minutes = end.getMinutes();
endTime.textContent = `종료 시간 ${hours < 10 ? '0' : ''}${hours} : ${
minutes < 10 ? '0' : ''
}${minutes}`;
}

function startTimer() {
const seconds = parseInt(this.dataset.time);
timer(seconds);
}

function quitTimer() {
clearInterval(countdown);
working = false;
}

buttons.forEach((button) => button.addEventListener('click', startTimer));
quit.addEventListener('click', quitTimer);
document.customForm.addEventListener('submit', submitSeconds);

function submitSeconds(e) {
e.preventDefault();
const inputMins = this.minutes.value;
timer(inputMins * 60);
this.reset();
}

Author

Hoonjoo

Posted on

2022-01-05

Updated on

2022-02-07

Licensed under

Comments