Таймер обратного отсчёта на JavaScript

Введение


Итак, первым делом нам нужно определить базовую HTML-структуру нашего таймера. Наверняка вы уже догадались, что для него нам нужно определить какой-либо блок-контейнер, давайте его зададим:

Код

<div class="timer-numbers" id="timer"></div>

Таймер будет состоять из 3-х разрядов: часы, минуты и секунды, разделённые между собой двоеточием.
Мы создадим 5 блоков span внутри контейнера:

Код

  <div class="timer-numbers" id="timer">
  <span class="hours">5</span>
  <span>:</span>
  <span class="minutes">15</span>
  <span>:</span>
  <span class="seconds">25</span>
  </div>

Блоку с таймингом часов присвоим класс .hours, блоку с минутами .minutes, с секундами .seconds
Вот такая нехитрая базовая структура у нашего таймера.

Переходим к написанию JavaScript кода, который будет оживлять наш таймер. Сейчас в таймере стоят произвольные начальные цифры (можете поставить любые другие, на данном этапе они ни на что не влияют)

Начинаем с обработчика события для объекта window, чтобы скрипт начал свою работу только после того, как загрузится весь HTML-костяк страницы (DOM-дерево) на которой будет размещён таймер:

Код

window.addEventListener('DOMContentLoaded', function() {
'use strict';
});

Теперь нам нужно определиться с датой, в которую таймер должен остановиться (а если точнее - обнулиться), для этого мы создадим переменную deadline и запишем в неё дату в формате ГОД - МЕСЯЦ - ЧИСЛО, я поставлю, к примеру, дату 15 мая 2020 года:

Код
let deadline = '2020-05-15';

Теперь нам необходимо написать функцию, у которой входным параметром будет эта дата остановки таймера.
Назовём функцию getTimeRemaining, а входной параметр(аргумент) назовём endtime:

Код

function getTimeRemaining(endtime) {

}

Забегая вперёд скажу, что этот аргумент endtime будет впоследствии принимать значение из переменной deadline (нашу дату остановки таймера)

Начинаем писать нашу функцию getTimeRemaining и в первой строке её кода мы создадим переменную t, в которую будет сохраняться разница между текущей датой и датой остановки таймера.

Чтобы получить эту разницу, нам предварительно с помощью метода Date.parse() нужно перевести эти даты из строкового представления в числовое.
Метод Date.parse() как раз это и делает и возвращает нам миллисекунды. Таким образом, в переменной t у нас сохранится разница между датами остановки таймера и текущей датой в миллисекундах.

Код

function getTimeRemaining(endtime) {
  let t = Date.parse(endtime) - Date.parse(new Date()),
}

Но в нашем таймере нам не нужны миллисекунды, нам нужны секунды, минуты и часы. Получить мы их можем с помощью метода Math.floor(), который округляет до целого числа выбранный аргумент.

Итак, мы будем использовать уже полученное значение миллисекунд (переменную t), чтобы получить секунды, минуты и часы.
Создадим переменную seconds :

Код

function getTimeRemaining(endtime) {
  let t = Date.parse(endtime) - Date.parse(new Date()),
  seconds = Math.floor((t/1000) % 60),
}

Как мы получили секунды? Мы взяли количество миллисекунд и разделили на 1000, так мы получили секунды.
Но, так как нам нужны именно секунды, то из этого полученного числа нам нужно вычленить минуты, чтобы остались именно секунды.
Поясню. Допустим, количество секунд у нас получилось 650. Как вы знаете, в одной целой минуте 60 секунд, вот нам и нужно удалить из этого числа все целые минуты, а всё, что останется после удаления и будут секунды.

Нам в этом поможет оператор со знаком %, который возвращает целый остаток от деления. То есть мы, к примеру, делим 650 % 60 и получаем остаток от деления 50. Это и будут секунды.
Какая цифра делится на 60 без остатка? правильно, 600 : 60 = 10 (и это целые минуты, нам они не нужны), поэтому мы их вычитаем:
650 - 600 = 50 и эта цифра на 60 уже не делится, поэтому это и будет остаток от деления, это и будут секунды.

Если вас это вводит в затруднение, обратитесь к данной теме.

Теперь мы создадим переменную minutes и в неё сохраним минуты аналогично используя метод Math.floor():

Код

function getTimeRemaining(endtime) {
  let t = Date.parse(endtime) - Date.parse(new Date()),
  seconds = Math.floor((t/1000) % 60),
  minutes = Math.floor((t/1000/60) % 60),
}

Аналогично, мы берём миллисекунды, делим на 1000 и получаем секунды, делим их на 60 и получаем минуты, так как в одном часе 60 минут, вычленяем из полученного числа целые часы, остаток от деления будут минуты.

И создадим также переменную hours, в которую будем сохранять часы:

Код

function getTimeRemaining(endtime) {
  let t = Date.parse(endtime) - Date.parse(new Date()),
  seconds = Math.floor((t/1000) % 60),
  minutes = Math.floor((t/1000/60) % 60),
  hours = Math.floor((t/(1000*60*60)));
}

Здесь немного проще: миллисекунды мы делением на 1000 превращаем в секунды, делением на 60 превращаем в минуты и делением ещё на 60 превращаем в часы.

Хорошо, мы получили исходные данные, давайте попросим функцию нам их вернуть в виде объекта:

Код

function getTimeRemaining(endtime) {
  let t = Date.parse(endtime) - Date.parse(new Date()),
  seconds = Math.floor((t/1000) % 60),
  minutes = Math.floor((t/1000/60) % 60),
  hours = Math.floor((t/(1000*60*60)));

  return {
  'total' : t,
  'hours' : hours,
  'minutes' : minutes,
  'seconds' : seconds
  };
}

В объекте у нас будут четыре пары ключ : значение, в которых сохранены значения ранее созданных нами переменных.
Таким образом, результатом работы нашей функции будет объект с данными, необходимыми для нашего таймера.

Но, есть одно но: таймер на то и таймер, что он отсчитывает время, а это значит, что показатели секунд, минут и часов должны изменяться, а точнее обновляться.

Для этого нам нужно написать ещё одну функцию, которая этим и будет заниматься. Назовём её setClock и у неё мы установим два аргумента id и endtime:

Код

function setClock(id, endtime) {

}

Для начала мы выберем все составляющие элементы нашего таймера и сохраним их в переменных в нашу функцию:

Код

function setClock(id, endtime) {
  let timer = document.getElementById(id),
  hours = timer.querySelector('.hours'),
  minutes = timer.querySelector('.minutes'),
  seconds = timer.querySelector('.seconds'),
}

И создадим ещё одну переменную, назовём её timeInterval, ей мы присвоим метод setInterval(), который будет вызывать функцию updateClock (мы её сейчас также напишем) каждые 1000 миллисекунд (то есть, каждую секунду):

Код

function setClock(id, endtime) {
  let timer = document.getElementById(id),
  hours = timer.querySelector('.hours'),
  minutes = timer.querySelector('.minutes'),
  seconds = timer.querySelector('.seconds'),
  timeInterval = setInterval(updateClock, 1000);
}

Что же будет делать эта функция updateClock, которую каждую секунду мы будем вызывать?
Всё просто: она будет обновлять показания времени для таймера, а именно - вызывать функцию getTimeRemaining, написанную нами ранее.

Код

function setClock(id, endtime) {
  let timer = document.getElementById(id),
  hours = timer.querySelector('.hours'),
  minutes = timer.querySelector('.minutes'),
  seconds = timer.querySelector('.seconds'),
  timeInterval = setInterval(updateClock, 1000);

  function updateClock() {
  let t = getTimeRemaining(endtime);
  }
}

Результатом работы функции getTimeRemaining, как вы помните, будет объект с данными, нужными для нашего таймера, мы сохранили его в переменную t.

Получается что происходит: мы каждую секунду вызываем функцию updateClock(), в результате её работы каждый раз получаем объект с обновлёнными данными для нашего таймера в рельном времени.

Нам остаётся только эти данные вывести вместо тех произвольных цифр, которые мы написали в вёрстке HTML-каркаса нашего таймера на начальном этапе.

Воспользуемся для этого свойством [i]textContent[/i], которое позволяет устанавливать элементу текстовый контент:

Код

  hours.textContent = t.hours;
  minutes.textContent = t.minutes;
  seconds.textContent = t.seconds;

Разберём подробнее. Мы берём переменную hours, созданную нами в функции setClock, значение этой переменной - это тег span с классом .hours, и теперь мы с помощью свойства textContent помещаем в этот тег span данные, которые берём из объекта, сохранённого в переменной t.

Аналогично мы помещаем значения минут и секунд в теги span с классами minutes и seconds соответственно.

Всё, наш таймер считайте заработал. Но, нам нужно его как то остановить при достижении нужной даты.

Воспользуемся для этого управляющей инструкцией, в которой отразим условие, что если разница между датой остановки таймера и текущей датой меньше или равна нулю, то мы вызываем функцию clearInterval(), отменяющую многократные повторения действий функции setInterval().

При этом вызывая функцию clearInterval(), мы передаём ей в качестве аргумента переменную timeInterval, поскольку именно она содержит в себе как значение вызов функции setInterval.

Код

  if (t.total <= 0) {
  clearInterval(timeInterval);
  }

И конечный штрих нашей программы, запускаем наш таймер, вызывая функцию setClock, передавая ей два аргумента: timer и deadline:

Код
setClock('timer', deadline);

Полный код скрипта на этот момент:

Код

window.addEventListener('DOMContentLoaded', function() {

  'use strict';
   
let deadline = '2020-05-15';
   
function getTimeRemaining(endtime) {
  let t = Date.parse(endtime) - Date.parse(new Date()),
  seconds = Math.floor((t/1000) % 60),
  minutes = Math.floor((t/1000/60) % 60),
  hours = Math.floor((t/(1000*60*60)));

  return {
  'total' : t,
  'hours' : hours,
  'minutes' : minutes,
  'seconds' : seconds
  };
}

function setClock(id, endtime) {
  let timer = document.getElementById(id),
  hours = timer.querySelector('.hours'),
  minutes = timer.querySelector('.minutes'),
  seconds = timer.querySelector('.seconds'),
  timeInterval = setInterval(updateClock, 1000);
   
  function updateClock() {
  let t = getTimeRemaining(endtime);

  hours.textContent = t.hours;
  minutes.textContent = t.minutes;
  seconds.textContent = t.seconds;

  if (t.total <= 0) {
  clearInterval(timeInterval);
  }
  }
}
setClock('timer', deadline);
});


Показания таймера и часовой пояс



Чтобы таймер корректно отображал оставшееся до его остановки время, необходимо в нашем скрипте учесть часовой пояс.
Например, если это Москва, то в первой строке нашего скрипта учитываем часовой пояс для Москвы:

Код

let deadline = '2020-05-11 00:00:00 GMT+0300';


Разрядность таймера при показаниях счётчиков менее 10 единиц



Чтобы при показаниях счётчиков таймера менее 10 единиц (например, 9 часов, 8 минут, 5 секунд) сохранялся двоичный разряд чисел (09 часов, 08 минут, 05 секунд) нужно дополнительно написать функцию, в которой будет проверяться условие, что если цифра счётчика меньше или равна 9, то для такой цифры перед ней добавлять в счётчик 0.

Данную функцию мы назовём addZero и поместим внутри нашей функции updateClock() сразу после строки с переменной t:

Код

  function updateClock() {
  let t = getTimeRemaining(endtime);

  function addZero(num){
  if(num <= 9) {
  return '0' + num;
  } else return num;
  };

  hours.textContent = addZero(t.hours);
  minutes.textContent = addZero(t.minutes);
  seconds.textContent = addZero(t.seconds);

  if (t.total <= 0) {
  clearInterval(timeInterval);
  }
  }

Функции addZero мы зададим один параметр под названием num, а в строках кода чуть ниже самой этой функции мы будем вызывать её, передавая ей в качестве аргумента значения из объекта.

Таким образом мы каждый раз сможем проверять, значения из объекта удовлетворяют условию функции addZero или нет.
Если условие сработает (истина), то в счётчике будет добавлен ноль, если условие не срабатывает, в счётчике цифра отображается как есть.

Обнуление счётчиков таймера при достижении заданной даты



Чтобы при достижении даты окончания работы таймера на его счётчиках отобразились нули, нужно в условии, стоящем в самом конце кода программы:

Код

  if (t.total <= 0) {
  clearInterval(timeInterval);
  }

добавить ещё три строки кода:

Код

  if (t.total <= 0) {
  clearInterval(timeInterval);
  hours.textContent = '00';
  minutes.textContent = '00';
  seconds.textContent = '00';
  }

Этими строками мы передаём в теги span уже не данные из объекта, а конкретные цифры, в данном случае нули.

Вот теперь наш счётчик готов! Полный код скрипта программы:

Код

window.addEventListener('DOMContentLoaded', function() {

  'use strict';
   
let deadline = '2020-05-15 00:00:00 GMT+0300';
   
function getTimeRemaining(endtime) {
  let t = Date.parse(endtime) - Date.parse(new Date()),
  seconds = Math.floor((t/1000) % 60),
  minutes = Math.floor((t/1000/60) % 60),
  hours = Math.floor((t/(1000*60*60)));

  return {
  'total' : t,
  'hours' : hours,
  'minutes' : minutes,
  'seconds' : seconds
  };
}

function setClock(id, endtime) {
  let timer = document.getElementById(id),
  hours = timer.querySelector('.hours'),
  minutes = timer.querySelector('.minutes'),
  seconds = timer.querySelector('.seconds'),
  timeInterval = setInterval(updateClock, 1000);
   
  function updateClock() {
  let t = getTimeRemaining(endtime);

  function addZero(num){
  if(num <= 9) {
  return '0' + num;
  } else return num;
  };

  hours.textContent = addZero(t.hours);
  minutes.textContent = addZero(t.minutes);
  seconds.textContent = addZero(t.seconds);

  if (t.total <= 0) {
  clearInterval(timeInterval);
  hours.textContent = '00';
  minutes.textContent = '00';
  seconds.textContent = '00';
  }
  }
}

setClock('timer', deadline);

});


Ссылка на GitHub

Комментарии к материалу: