본문 바로가기

JavaScript30 Challenge

[JavaScript30] Day 29

Countdown Timer

 

function timer(seconds) {
    setInterval(function() {
        seconds--;
    }, 1000);    
}

이렇게 매 초마다 1초씩 줄어들게 하는 것은 가끔 작동하지 않는 오류가 있다.

예를 들어 해당 탭을 오랫동안 켜지않을 경우에는 타이머가 멈추게 된다.

 

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    console.log({now, then});
}

then에서 1000을 곱해준것은 초를 밀리초로 바꾸기 위해서 이다. ( 1s = 1000ms )

 

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;

    setInterval(() => {
        const secondsLeft = (then - Date.now()) / 1000;
        console.log(secondsLeft);
    }, 1000);
}

초가 정수로 딱 떨어지지 않고, 0초를 지나 -초가 출력된다.

 

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;

    setInterval(() => {
        const secondsLeft = Math((then - Date.now()) / 1000);
        console.log(secondsLeft);
    }, 1000);
}

Math.round()를 사용해서 초가 정수로 나오도록 한다.

 

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;

    setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            return;
        }
        // display it
        console.log(secondsLeft);
    }, 1000);
}

이제 1초를 지나면 타이머가 멈추는 작업을 한다.

위와 같이 할 경우 출력해보면 1을 마지막으로 더 이상 출력은 안되지만 실제로 타이머는 계속 작동하고 있다.

 

let countdown;

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        console.log(secondsLeft);
    }, 1000);
}

그래서  setInterval 에 countdown이라는 이름으로 저장하고, countdown clearInterval 로 제거해준다.

타이머는 남은 초가 0 초가 되면 작동을 멈춘다.

 

하지만 한 가지 문제점은 3초에서 시작했지만, 3은 출력되지 않는다.

측 시작하는 초를 출력하지 못하는 문제점이 있다.

let countdown;

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    displayTimeLeft(seconds);

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        displayTimeLeft(secondsLeft);
    }, 1000);
}

function displayTimeLeft(seconds) {
    console.log(seconds);    
}

출력을 displayTimeLeft라는 function을 만들어서 그 안에서 이뤄지게 한다.

타이머가 시작할 때 시작하는 초를 출력하고, 타이머가 작동하면서는 남은 초를 출력하게 한다.

 

let countdown;

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    displayTimeLeft(seconds);

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        displayTimeLeft(secondsLeft);
    }, 1000);
}

function displayTimeLeft(seconds) {
    const minutes = Math.floor(seconds / 60);
    const remainderSeconds = seconds % 60;    
    console.log({minutes, remainderSeconds});
}

분과 초로 출력될 수 있도록 한다.

 

이제 콘솔 창에서 확인해보던걸 화면에 보이도록 해준다.

let countdown;
const timerDisplay = document.querySelector('.display__time-left');

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    displayTimeLeft(seconds);

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        displayTimeLeft(secondsLeft);
    }, 1000);
}

function displayTimeLeft(seconds) {
    const minutes = Math.floor(seconds / 60);
    const remainderSeconds = seconds % 60;
    const display = `${minutes}:${remainderSeconds}`;
    timerDisplay.textContent = display;
}

화면에 보이도록 한 후 확인해보니 어색한 느낌이 든다.

2 : 3이라고 출력되고 있는데 2 : 03이라고 출력되는 것을 원한다.

 

let countdown;
const timerDisplay = document.querySelector('.display__time-left');

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    displayTimeLeft(seconds);

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        displayTimeLeft(secondsLeft);
    }, 1000);
}

function displayTimeLeft(seconds) {
    const minutes = Math.floor(seconds / 60);
    const remainderSeconds = seconds % 60;
    const display = `${minutes}:${remainderSeconds < 10 ? '0' : ''}${remainderSeconds}`;
    timerDisplay.textContent = display;
}

 ${remainderSeconds < 10 ? '0' : ''}${remainderSeconds} 라고 작성하여 10초보다 작은 경우에는 앞에 0을 붙여준다.

 

let countdown;
const timerDisplay = document.querySelector('.display__time-left');

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    displayTimeLeft(seconds);

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        displayTimeLeft(secondsLeft);
    }, 1000);
}

function displayTimeLeft(seconds) {
    const minutes = Math.floor(seconds / 60);
    const remainderSeconds = seconds % 60;
    const display = `${minutes}:${remainderSeconds < 10 ? '0' : ''}${remainderSeconds}`;
    document.title = display;
    timerDisplay.textContent = display;
}

 document.title = display 을 사용하여 tab에도 시간이 동일하게 나오게 한다.

 

이번에는 타이머가 끝나는 시간을 표시해주는 작업은 한다.

 Date.now() 로 얻은 숫자는 날짜와 초단위 시간까지 알 수 있는 정보이다.

 

const endTime = document.querySelector('.display__end-time');

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    displayTimeLeft(seconds);
    displayEndTime(then);

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        displayTimeLeft(secondsLeft);
    }, 1000);
}

function displayEndTime(timestamp) {
    const end = new Date(timestamp);
    const hour = end.getHours();
    const minutes = end.getMinutes();
    endTime.textContent = `Be Back At ${hour}:${minutes}`;
}

displayEndTime function을 만들어서 타이머가 끝나는 시간이 출력되도록 한다.

끝나는 시간은 timer function의 then에 저장된 정보를 사용한다.

 

const endTime = document.querySelector('.display__end-time');

function timer(seconds) {
    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    displayTimeLeft(seconds);
    displayEndTime(then);

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        displayTimeLeft(secondsLeft);
    }, 1000);
}

function displayEndTime(timestamp) {
    const end = new Date(timestamp);
    const hour = end.getHours();
    const adjustedHour = hour > 12 ? hour - 12 : hour;
    const minutes = end.getMinutes();
    endTime.textContent = `Be Back At ${adjustedHour}:${minutes < 10 ? '0' : ''}${minutes}`;
}

 

const buttons = document.querySelectorAll('[data-time]');

function startTimer() {
    console.log(this);
}

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

 

const buttons = document.querySelectorAll('[data-time]');

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

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

이 상태에서 버튼을 한번 클릭하면 제대로 나온다.

하지만 여러 번 클릭하게 되면 timer function이 여러번 작동하게 되어 시간이 왔다 갔다 변화한다.

이를 해결하기 위해서는 우리가 다른 버튼을 클릭했을 때 기존의 타이머는 삭제해주는 것이다.

 

let countdown;
const timerDisplay = document.querySelector('.display__time-left');
const endTime = document.querySelector('.display__end-time');
const buttons = document.querySelectorAll('[data-time]');

function timer(seconds) {
    //clean any existing timers.
    clearInterval(countdown);

    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    displayTimeLeft(seconds);
    displayEndTime(then);

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft <= 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        displayTimeLeft(secondsLeft);
    }, 1000);
}

function displayTimeLeft(seconds) {
    const minutes = Math.floor(seconds / 60);
    const remainderSeconds = seconds % 60;
    const display = `${minutes}:${remainderSeconds < 10 ? '0' : ''}${remainderSeconds}`;
    document.title = display;
    timerDisplay.textContent = display;
}

function displayEndTime(timestamp) {
    const end = new Date(timestamp);
    const hour = end.getHours();
    const adjustedHour = hour > 12 ? hour - 12 : hour;
    const minutes = end.getMinutes();
    endTime.textContent = `Be Back At ${adjustedHour}:${minutes < 10 ? '0' : ''}${minutes}`;
}

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

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

timer을 시작하면서 countdown을 clear해준다.

이전에 어떤 timer가 있어도 삭제를 하고 시작하기 때문에 아까와 같이 여러 개가 겹치지 않는다.

 

 

오른쪽 상단에 Enter Minutes라는 input창에 분을 입력하여 타이머를 작동하는 작업을 해봅니다.

document.customForm.addEventListener('submit', function(e) {
    e.preventDefault();
    const mins = this.minutes.value;
    console.log(mins);
});

 document.formName 을 통해서 해당하는 form을 선택할 수 있다.

input창에 분을 입력했을 때, 전송되는 것을 막기 위해  e.preventDefault(); 를 사용한다.

입력된 값을  mins 에 저장한다.

 

document.customForm.addEventListener('submit', function(e) {
    e.preventDefault();
    const mins = this.minutes.value;
    timer(mins * 60);
    this.reset();
});

전달받은 값을 timer function에 넣어 작동시키는데,

입력한 값이 분이기 때문에 60을 곱해서 초로 만들어 준다.

그리고 reset을 이용하여 input창에 입력한 값이 남아있지 않도록 해준다.

 

최종 코드

let countdown;
const timerDisplay = document.querySelector('.display__time-left');
const endTime = document.querySelector('.display__end-time');
const buttons = document.querySelectorAll('[data-time]');

function timer(seconds) {
    //clean any existing timers.
    clearInterval(countdown);

    const now = Date.now();  // Date.now() = (new Date()).getTime();
    const then = now + seconds * 1000;
    displayTimeLeft(seconds);
    displayEndTime(then);

    countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);
        // check if we should stop ot!
        if (secondsLeft < 0) {
            clearInterval(countdown);
            return;
        }
        // display it
        displayTimeLeft(secondsLeft);
    }, 1000);
}

function displayTimeLeft(seconds) {
    const minutes = Math.floor(seconds / 60);
    const remainderSeconds = seconds % 60;
    const display = `${minutes}:${remainderSeconds < 10 ? '0' : ''}${remainderSeconds}`;
    document.title = display;
    timerDisplay.textContent = display;
}

function displayEndTime(timestamp) {
    const end = new Date(timestamp);
    const hour = end.getHours();
    const adjustedHour = hour > 12 ? hour - 12 : hour;
    const minutes = end.getMinutes();
    endTime.textContent = `Be Back At ${adjustedHour}:${minutes < 10 ? '0' : ''}${minutes}`;
}

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

buttons.forEach(button => button.addEventListener('click', startTimer));
document.customForm.addEventListener('submit', function(e) {
    e.preventDefault();
    const mins = this.minutes.value;
    timer(mins * 60);
    this.reset();
});

 

 

 

 


parseInt() : 문자열 인자를 구문 분석하여 특정 진수의 정수를 반환하는 함수

HTMLElement.dataset : 요소의 사용자 지정 데이터 특성(data-*)에 대한 읽기와 쓰기 접근 방법을 HTML과 DOM 양측에 제공하는 읽기 전용 속성 

 

 

JavaScript30 강의를 보고 공부한 글입니다. (https://javascript30.com/)

'JavaScript30 Challenge' 카테고리의 다른 글

[JavaScript30] Day 30  (0) 2020.08.29
[JavaScript30] Day 28  (0) 2020.08.28
[JavaScript30] Day 27  (0) 2020.08.27
[JavaScript30] Day 26  (0) 2020.08.20
[JavaScript30] Day 25  (0) 2020.08.19