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

Александр Мальцев
Александр Мальцев
32K
32
Таймер обратного отсчёта на чистом JavaScript
Содержание:
  1. Демо таймера обратного отсчёта
  2. Подключение и настройка таймера
  3. Структура кода JavaScript
  4. Скрипт для создания нескольких таймеров отчета на странице
  5. Комментарии

В этой статье рассмотрим таймер обратного отсчета, построенный на чистом CSS и JavaScript. Он написан с использованием минимального количества кода без использования jQuery и каких-либо других сторонних библиотек.

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

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

Простой таймер обратного отсчета с днями, часами, минутами и секундами. Очень легко настраивается. Создан на чистом CSS и Javascript (без зависимостей).

Простой таймер обратного отсчета с днями, часами, минутами и секундами на чистом CSS и JavaScript

Посмотреть

Подключение и настройка таймера

1. Вставить в нужное место страницы html-разметку таймера:

<div class="timer">
  <div class="timer__items">
    <div class="timer__item timer__days">00</div>
    <div class="timer__item timer__hours">00</div>
    <div class="timer__item timer__minutes">00</div>
    <div class="timer__item timer__seconds">00</div>
  </div>
</div>

Таймер обратного отсчета отображает четыре числа: дни, часы, минуты и секунды. Каждое число находится в элементе <div>. Первый класс (timer__item) используется для стилизации элемента, а второй - для таргетинга в JavaScript.

2. Добавить стили (базовое оформление):

.timer__items {
  display: flex;
  font-size: 48px;
}
.timer__item {
  position: relative;
  min-width: 60px;
  margin-left: 10px;
  margin-right: 10px;
  padding-bottom: 15px;
  text-align: center;
}
.timer__item::before {
  content: attr(data-title);
  display: block;
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  font-size: 14px;
}
.timer__item:not(:last-child)::after {
  content: ':';
  position: absolute;
  right: -15px;
}

Стилизовать таймер обратного отсчета можно так как вы этого хотите.

Вышеприведённый CSS использует flexbox. Знак «:» и текст под каждым компонентом даты выводиться на страницу с помощью псевдоэлементов.

3. Добавить JavaScript:

document.addEventListener('DOMContentLoaded', function() {
  // конечная дата, например 1 июля 2021
  const deadline = new Date(2021, 06, 01);
  // id таймера
  let timerId = null;
  // склонение числительных
  function declensionNum(num, words) {
    return words[(num % 100 > 4 && num % 100 < 20) ? 2 : [2, 0, 1, 1, 1, 2][(num % 10 < 5) ? num % 10 : 5]];
  }
  // вычисляем разницу дат и устанавливаем оставшееся времени в качестве содержимого элементов
  function countdownTimer() {
    const diff = deadline - new Date();
    if (diff <= 0) {
      clearInterval(timerId);
    }
    const days = diff > 0 ? Math.floor(diff / 1000 / 60 / 60 / 24) : 0;
    const hours = diff > 0 ? Math.floor(diff / 1000 / 60 / 60) % 24 : 0;
    const minutes = diff > 0 ? Math.floor(diff / 1000 / 60) % 60 : 0;
    const seconds = diff > 0 ? Math.floor(diff / 1000) % 60 : 0;
    $days.textContent = days < 10 ? '0' + days : days;
    $hours.textContent = hours < 10 ? '0' + hours : hours;
    $minutes.textContent = minutes < 10 ? '0' + minutes : minutes;
    $seconds.textContent = seconds < 10 ? '0' + seconds : seconds;
    $days.dataset.title = declensionNum(days, ['день', 'дня', 'дней']);
    $hours.dataset.title = declensionNum(hours, ['час', 'часа', 'часов']);
    $minutes.dataset.title = declensionNum(minutes, ['минута', 'минуты', 'минут']);
    $seconds.dataset.title = declensionNum(seconds, ['секунда', 'секунды', 'секунд']);
  }
  // получаем элементы, содержащие компоненты даты
  const $days = document.querySelector('.timer__days');
  const $hours = document.querySelector('.timer__hours');
  const $minutes = document.querySelector('.timer__minutes');
  const $seconds = document.querySelector('.timer__seconds');
  // вызываем функцию countdownTimer
  countdownTimer();
  // вызываем функцию countdownTimer каждую секунду
  timerId = setInterval(countdownTimer, 1000);
});

4. Установить дату окончания. Например, до 1 июля 2021:

// конечная дата
const deadline = new Date(2021, 06, 01);

Структура кода JavaScript

Основную часть кода занимает функция countdownTimer:

function countdownTimer() {
  // ...
}

Эта функция выполняет расчёт оставшегося времени и обновляет содержимое элементов .timer__item на странице.

Расчёт оставшегося времени осуществляется посредством вычитания текущей даты из конечной:

// new Date() - текущая дата и время
const diff = deadline - new Date();

Вычисление оставшегося количества дней, часов, минут и секунд выполняется следующим образом:

const days = diff > 0 ? Math.floor(diff / 1000 / 60 / 60 / 24) : 0;
const hours = diff > 0 ? Math.floor(diff / 1000 / 60 / 60) % 24 : 0;
const minutes = diff > 0 ? Math.floor(diff / 1000 / 60) % 60 : 0;
const seconds = diff > 0 ? Math.floor(diff / 1000) % 60 : 0;

Встроенная функция Math.floor используется для округления числа до ближайшего целого (посредством отбрасывания дробной части).

Вывод оставшегося времени на страницу:

$days.textContent = days < 10 ? '0' + days : days;
$hours.textContent = hours < 10 ? '0' + hours : hours;
$minutes.textContent = minutes < 10 ? '0' + minutes : minutes;
$seconds.textContent = seconds < 10 ? '0' + seconds : seconds;
$days.dataset.title = declensionNum(days, ['день', 'дня', 'дней']);
$hours.dataset.title = declensionNum(hours, ['час', 'часа', 'часов']);
$minutes.dataset.title = declensionNum(minutes, ['минута', 'минуты', 'минут']);
$seconds.dataset.title = declensionNum(seconds, ['секунда', 'секунды', 'секунд']);

Переменные $days, $hours, $minutes, $seconds содержат элементы (таргеты), в которые выводятся компоненты времени.

Изменение содержимого элементов выполняется через textContent. Если значение меньше 10, то к нему добавляется символ «0».

Получение элементов (выполняется с помощью querySelector):

// получаем элементы, содержащие компоненты даты
const $days = document.querySelector('.timer__days');
const $hours = document.querySelector('.timer__hours');
const $minutes = document.querySelector('.timer__minutes');
const $seconds = document.querySelector('.timer__seconds');

Функция declensionNum используется для склонения числительных:

// склонение числительных
function declensionNum(num, words) {
  return words[(num % 100 > 4 && num % 100 < 20) ? 2 : [2, 0, 1, 1, 1, 2][(num % 10 < 5) ? num % 10 : 5]];
}

Для постоянного вычисления оставшегося времени и вывода его на страницу используется setInterval.

Хранение идентификатора таймера осуществляется в переменной timerId:

// id таймера
let timerId = null;

Использование setInterval для запуска функции countdownTimer каждую секунду:

// вызываем функцию countdownTimer каждую 1 секунду
timerId = setInterval(countdownTimer, 1000);

Остановка таймера по истечении времени выполняется в функции countdownTimer:

function countdownTimer() {
  ...
  if (diff <= 0) {
    // останавливаем таймер timerId
    clearInterval(timerId);
  }
  ...
}

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

Скрипт, написанный с использованием классов, который можно использовать для создания нескольких таймеров отчета на странице:

// класс для создание таймера обратного отсчета
class CountdownTimer {
  constructor(deadline, cbChange, cbComplete) {
    this._deadline = deadline;
    this._cbChange = cbChange;
    this._cbComplete = cbComplete;
    this._timerId = null;
    this._out = {
      days: '', hours: '', minutes: '', seconds: '',
      daysTitle: '', hoursTitle: '', minutesTitle: '', secondsTitle: ''
    };
    this._start();
  }
  static declensionNum(num, words) => {
    return words[(num % 100 > 4 && num % 100 < 20) ? 2 : [2, 0, 1, 1, 1, 2][(num % 10 < 5) ? num % 10 : 5]];
  }
  _start() {
    this._calc();
    this._timerId = setInterval(this._calc.bind(this), 1000);
  }
  _calc() {
    const diff = this._deadline - new Date();
    const days = diff > 0 ? Math.floor(diff / 1000 / 60 / 60 / 24) : 0;
    const hours = diff > 0 ? Math.floor(diff / 1000 / 60 / 60) % 24 : 0;
    const minutes = diff > 0 ? Math.floor(diff / 1000 / 60) % 60 : 0;
    const seconds = diff > 0 ? Math.floor(diff / 1000) % 60 : 0;
    this._out.days = days < 10 ? '0' + days : days;
    this._out.hours = hours < 10 ? '0' + hours : hours;
    this._out.minutes = minutes < 10 ? '0' + minutes : minutes;
    this._out.seconds = seconds < 10 ? '0' + seconds : seconds;
    this._out.daysTitle = CountdownTimer.declensionNum(days, ['день', 'дня', 'дней']);
    this._out.hoursTitle = CountdownTimer.declensionNum(hours, ['час', 'часа', 'часов']);
    this._out.minutesTitle = CountdownTimer.declensionNum(minutes, ['минута', 'минуты', 'минут']);
    this._out.secondsTitle = CountdownTimer.declensionNum(seconds, ['секунда', 'секунды', 'секунд']);
    this._cbChange ? this._cbChange(this._out) : null;
    if (diff <= 0) {
      clearInterval(this._timerId);
      this._cbComplete ? this._cbComplete() : null;
    }
  }
}

Пример использования класса CountdownTimer() для создания таймера на странице:

// 1. Получим элементы в которые нужно вывести оставшееся количество дней, часов, минут и секунд
const elDays1 = document.querySelector('.timer-1 .timer__days');
const elHours1 = document.querySelector('.timer-1 .timer__hours');
const elMinutes1 = document.querySelector('.timer-1 .timer__minutes');
const elSeconds1 = document.querySelector('.timer-1 .timer__seconds');

// 2. Установим время, например, на одну минуту от текущей даты
const deadline1 = new Date(Date.now() + (60 * 1000 + 999));

// 3. Создадим новый объект, используя new CountdownTimer()
new CountdownTimer(deadline1, (timer) => {
  elDays1.textContent = timer.days;
  elHours1.textContent = timer.hours;
  elMinutes1.textContent = timer.minutes;
  elSeconds1.textContent = timer.seconds;
  elDays1.dataset.title = timer.daysTitle;
  elHours1.dataset.title = timer.hoursTitle;
  elMinutes1.dataset.title = timer.minutesTitle;
  elSeconds1.dataset.title = timer.secondsTitle;
}, () => {
  document.querySelector('.timer-1 .timer__result').textContent = 'Таймер завершился!';
});

В new CountdownTimer() необходимо передать следующие аргументы:

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

HTML код первого таймера:

<div class="timer timer-1">
  <div class="timer__items">
    <div class="timer__item timer__days">00</div>
    <div class="timer__item timer__hours">00</div>
    <div class="timer__item timer__minutes">00</div>
    <div class="timer__item timer__seconds">00</div>
  </div>
  <div class="timer__result"></div>
</div>

Инициализация остальных таймеров на странице с помощью new CountdownTimer() выполняется аналогично.

Пример страницы, на которой имеется несколько таймеров обратного отсчёта:

Несколько таймеров обратного отсчета на одной странице

Посмотреть

Комментарии: 32

  1. kuwialcs
    kuwialcs
    2022-09-09 19:58:22
    А как сделать чтобы он считал в другое направление? Секундомер типо.
  1. Александр Мальцев
    Александр Мальцев
    2022-09-16 12:36:35
    Нужно поменять пару строк. Пример как можно это сделать: открыть.
  • Григорий
    Григорий
    2022-07-04 23:53:17
    Александр, добрый день!
    Подскажите, вынес js код в отдельный файл, как его подключить в HTML коде?
    1. Александр Мальцев
      Александр Мальцев
      2022-07-05 13:19:02
      Добрый день!
      Как подключить JavaScript файл к странице подробно описано здесь.
    2. Григорий
      Григорий
      2022-07-05 14:44:21
      Просто не совсем понимаю, как будет выглядеть вызов скрипта в данном случае. Как скрипт подключить через <script src=" я разобрался, а вот вызвать его не могу(
    3. Александр Мальцев
      Александр Мальцев
      2022-07-07 12:59:00
      Ну где-то нужно вызвать. Либо в том же файле, или в другом, или просто на странице в <script>.
  • Dmitry
    Dmitry
    2022-04-02 22:10:00
    Подскажите, как можно добавить телеграмм бота к таймеру? Нужно что бы бот присылал уведомление в чат по завершению таймера. Буду очень признателен за помощь. 2 дня уже бьюсь, нашел как сделать отправку с сайта, но это происходит когда я на странице и если встроить в событие таймера, то происходит отправка и все таймеры рушатся.
    1. Андрей
      Андрей
      2022-02-28 16:13:20
      Здравствуйте, Александр!
      Нужна Ваша помощь. На своем проекте я использую следующий код таймера:
      document.addEventListener('DOMContentLoaded', () => {
        // конечная дата
        const deadline = new Date(new Date().getFullYear(), new Date().getMonth() + 1, 01);
        // id таймера
        let timerId = null;
        // склонение числительных
        function declensionNum(num, words) {
          return words[(num % 100 > 4 && num % 100 < 20) ? 2 : [2, 0, 1, 1, 1, 2][(num % 10 < 5) ? num % 10 : 5]];
        }
        // вычисляем разницу дат и устанавливаем оставшееся времени в качестве содержимого элементов
        function countdownTimer() {
          const diff = deadline - new Date();
          if (diff <= 0) {
            clearInterval(timerId);
          }
          const days = diff > 0 ? Math.floor(diff / 1000 / 60 / 60 / 24) : 0;
          const hours = diff > 0 ? Math.floor(diff / 1000 / 60 / 60) % 24 : 0;
          const minutes = diff > 0 ? Math.floor(diff / 1000 / 60) % 60 : 0;
          const seconds = diff > 0 ? Math.floor(diff / 1000) % 60 : 0;
          $days.textContent = days < 10 ? '0' + days : days;
          $hours.textContent = hours < 10 ? '0' + hours : hours;
          $minutes.textContent = minutes < 10 ? '0' + minutes : minutes;
          $seconds.textContent = seconds < 10 ? '0' + seconds : seconds;
          $days.dataset.title = declensionNum(days, ['', '', '']);
          $hours.dataset.title = declensionNum(hours, ['', '', '']);
          $minutes.dataset.title = declensionNum(minutes, ['', '', '']);
          $seconds.dataset.title = declensionNum(seconds, ['', '', '']);
        }
        // получаем элементы, содержащие компоненты даты
        const $days = document.querySelector('.timer__days1');
        const $hours = document.querySelector('.timer__hours1');
        const $minutes = document.querySelector('.timer__minutes1');
        const $seconds = document.querySelector('.timer__seconds1');
        // вызываем функцию countdownTimer
        countdownTimer();
        // вызываем функцию countdownTimer каждую секунду
        timerId = setInterval(countdownTimer, 1000);
      });
      Данный таймер должен показывать сколько времени осталось до 00:00 и затем возобновлять отсчет (быть зацикленным), но по какой-то причине этого не происходит. Помогите, пожалуйста, разобраться.
      1. Александр Мальцев
        Александр Мальцев
        2022-03-03 14:21:53
        Добрый день! Воспользуйтесь скриптом, приведённом в этом комментарии.
    2. UIkit
      UIkit
      2022-02-28 02:08:56
      можно ли по завершении отсчета придумать функцию для добавления атрибута hidden к одному блоку и удаления у другого?
      1. Александр Мальцев
        Александр Мальцев
        2022-03-03 14:25:22
        new CountdownTimer(deadline1, (timer) => {
          // ...
        }, () => {
          // ... действия при завершении отчёта (здесь можно написать добавления атрибута hidden для одного блока и удаления для другого
        });
    3. dm-web
      dm-web
      2022-02-01 13:46:24
      Добрый день.
      Подскажите как зациклить отсчет.
      Например время 30 минут и по его окончанию таймер перезапускался и отсчет начинался по новой.
      Заранее спасибо.
      1. Александр Мальцев
        Александр Мальцев
        2022-02-02 14:02:31
        Привет! Для этого можно воспользоваться рекурсией. Ссылка на пример: countdown-timer-06.
    4. Вячеслав
      Вячеслав
      2022-01-30 13:49:01
      Александр, подскажите пожалуйста, как в одном таймере подключить 2 deadline, чтобы один срабатывал после окончания второго? Например какое-то время до начала мероприятия, а по его окончании сразу какое-то время до конца мероприятия.
      1. Александр Мальцев
        Александр Мальцев
        2022-01-30 14:51:34
        В качестве третьего аргумента CountdownTimer можно передать функцию которая будет выполнена после завершения таймера. Если туда передать новый таймер, то он запуститься сразу после окончания первого.
        Например, запустим первый таймер на 10 секунд, а после его завершения ещё один таймер на 10 секунд (открыть)
        const elDays1 = document.querySelector('.timer-1 .timer__days');
        const elHours1 = document.querySelector('.timer-1 .timer__hours');
        const elMinutes1 = document.querySelector('.timer-1 .timer__minutes');
        const elSeconds1 = document.querySelector('.timer-1 .timer__seconds');
        const deadline1 = new Date(Date.now() + (10 * 1000 + 999));
        new CountdownTimer(deadline1, (timer) => {
          elDays1.textContent = timer.days;
          elHours1.textContent = timer.hours;
          elMinutes1.textContent = timer.minutes;
          elSeconds1.textContent = timer.seconds;
          elDays1.dataset.title = timer.daysTitle;
          elHours1.dataset.title = timer.hoursTitle;
          elMinutes1.dataset.title = timer.minutesTitle;
          elSeconds1.dataset.title = timer.secondsTitle;
        }, () => {
          const deadline1 = new Date(Date.now() + (10 * 1000 + 999));
          new CountdownTimer(deadline1, (timer) => {
            elDays1.textContent = timer.days;
            elHours1.textContent = timer.hours;
            elMinutes1.textContent = timer.minutes;
            elSeconds1.textContent = timer.seconds;
            elDays1.dataset.title = timer.daysTitle;
            elHours1.dataset.title = timer.hoursTitle;
            elMinutes1.dataset.title = timer.minutesTitle;
            elSeconds1.dataset.title = timer.secondsTitle;
          })
        });
      2. Вячеслав
        Вячеслав
        2022-01-30 16:01:40
        Огромное спасибо! То, что надо!
    5. Вячеслав
      Вячеслав
      2022-01-27 19:49:57
      А как помимо конечной даты добавить конечное время. Например до начала какого-то события (концерта, турнира, лекции и т.п.)?
      1. Вячеслав
        Вячеслав
        2022-01-27 20:40:44
        Сам разобрался. Может кому поможет
        // конечная дата 28.01.22 12.00
        const deadline = (function(y, m, d, h) { return new Date(y, m-1, d, h); })(2022, 01, 28, 12);
    6. Сергей Плахотин
      Сергей Плахотин
      2021-12-30 22:00:41
      Добрый день, подскажите как можно модернизировать скрипт, что бы была возможность реализовывать множество таймеров на одной странице и с разными deadline. Заранее спасибо. Как на картинке:
      1. Александр Мальцев
        Александр Мальцев
        2021-12-31 09:12:05
        Привет! Переписал скрипт на классах и добавил его в статью. Его можно использовать для создания множества таймеров на странице.
        Пример вызова:
        // deadline - конечная дата
        // cb1 - функция обратного вызова, которая будет вызываться каждую секунду и которую можно использовать для обновления содержимого элементов, содержащих оставшееся время
        // cb2 - функция обратного вызова, которая будет запущена после завершения таймера 
        new CountdownTimer(deadline, cb1, cb2);
      2. Сергей Плахотин
        Сергей Плахотин
        2021-12-31 14:16:37
        Спасибо огромное! С наступающим новым годом!
    7. Anna Nifantova
      Anna Nifantova
      2021-12-20 22:57:13
      Подскажите как сделать чтобы «день, час, минута, секунда» были английскими?
      1. Александр Мальцев
        Александр Мальцев
        2021-12-24 13:30:51
        Необходимо просто указать их на английском (пример).
      2. Anna Nifantova
        Anna Nifantova
        2021-12-24 13:59:29
        Спасибо большое. Главное тут было переписать правильно склонение числительных. Я бы не смогла.
    8. Aid
      Aid
      2021-12-18 18:30:16
      Привет, а можно сделать так чтобы таймер был 30 минутный, что для этого надо сделать?
      1. Александр Мальцев
        Александр Мальцев
        2021-12-19 03:35:33
        Привет!
        Нужно просто конечную дату установить на 30 минут больше, чем текущую, ну и убрать ненужный код, связанный с часами и днями (пример).
      2. Aid
        Aid
        2021-12-19 21:08:16
        Спасибо)
    9. MI-5
      MI-5
      2021-08-10 13:23:56
      Добрый день. Дни неправильно считаются. Ставлю на следующий день — показывает на месяц больше
      1. Александр Мальцев
        Александр Мальцев
        2021-08-10 14:11:41
        Привет! В JavaScript месяц отсчитывается с нуля:
        // 5 января 2021
        new Date(2021, 0, 5);
        // 5 февраля 2021
        new Date(2021, 1, 5);
        // 5 марта 2021
        new Date(2021, 2, 5);
        
      2. MI-5
        MI-5
        2021-08-10 14:16:33
        Спасибо, но логичнее где-то изменить в скрипте. Тогда наверное учёт високосных годов сломается.

        что-то типа такого:
        var deadline = new Date(2021, 08, 11);
        deadline.setMonth(deadline.getMonth() — 1);

        и ставить уже нормальный месяц.

        В любом случае спасибо большое.
      3. Александр Мальцев
        Александр Мальцев
        2021-08-11 16:27:39
        Можно сделать так:
        // конечная дата
        const deadline = (function(y, m, d) { return new Date(y, m-1, d); })(2021, 08, 12);
        Т.е. использовать её вместо этой:
        const deadline = new Date(2021, 07, 12);
      4. Ирина
        Ирина
        2022-06-22 17:46:52
        Нормально работает, если поставить так:
        const deadline1 = new Date('2022/07/01');