Счётчик обратного отсчёта в "Списке новостей" Битрикса
8 мин 142 Обновлено 15 октября Сложность: Средняя
TL;DR
- Свойство "Дата/время" TIME_EVENT отмечаем в "Источнике данных", чтобы оно попало в DISPLAY_PROPERTIES.
- Дату отдаём в JS как Unix-timestamp в миллисекундах через data-атрибут – не зависит от формата и локали.
- Таймер пишем на чистом JavaScript (const/let), без jQuery.countdown.
- Каждый тик считает разницу от Date.now(), поэтому счётчик не "плывёт".
- Русское склонение "день/дня/дней" выбирает функция plural по последней цифре.
Задача: вывести ближайшее событие компонентом "Список новостей" и показать под ним таймер "до старта осталось 3 дня 4 часа". Дату события берём из свойства элемента, а сам отсчёт – на чистом JavaScript, без внешней библиотеки. Раньше для этого подключали jQuery.countdown, но в 2026 году отдельная библиотека и сам jQuery ради одного таймера уже избыточны.
Шаг 1. Свойство и настройка компонента
В инфоблоке "Мероприятия" создаём свойство "Дата мероприятия" типа "Дата/время" с кодом TIME_EVENT – из него таймер возьмёт дедлайн. Дальше настраиваем компонент bitrix:news.list:
- количество элементов на странице – 1 (нужно ближайшее событие);
- сортировка – по дате начала активности или по свойству
TIME_EVENT, чтобы "ближайшее" было первым; - в блоке "Источник данных" отмечаем свойство
TIME_EVENT, иначе оно не попадёт вDISPLAY_PROPERTIES.
Шаг 2. Отдаём дату в HTML через data-атрибут
Копируем шаблон компонента в свой шаблон сайта. В нём переводим значение свойства в Unix-timestamp через MakeTimeStamp() и кладём его в data-атрибут блока таймера. Timestamp – самый надёжный способ передать момент времени в JavaScript: он не зависит от формата даты и локали.
<?php foreach ($arItem['DISPLAY_PROPERTIES'] as $pid => $arProperty): ?>
<?php if ($pid === 'TIME_EVENT'): ?>
<?php $eventTs = MakeTimeStamp($arProperty['VALUE']); ?>
<div class="event__date">
<?= FormatDate('d F Y в H:i', $eventTs) ?> <?php // читаемая дата для человека ?>
</div>
<!-- дедлайн для JS: timestamp в миллисекундах -->
<div class="event__counter" data-deadline="<?= $eventTs * 1000 ?>"></div>
<?php endif ?>
<?php endforeach ?>
Здесь MakeTimeStamp переводит строку из свойства в timestamp (секунды), а * 1000 даёт миллисекунды – именно в них работает Date в JavaScript. Заодно выводим человеку понятную дату через FormatDate('d F Y в H:i', ...).
Шаг 3. Таймер на чистом JavaScript
В отдельном JS-файле забираем дедлайн из data-атрибута и раз в секунду пересчитываем остаток. Никакого jQuery – только современный синтаксис (const/let, стрелочные функции):
const box = document.querySelector('.event__counter');
if (box) {
const deadline = Number(box.dataset.deadline);
// склонение: plural(5, ['день','дня','дней']) → 'дней'
const plural = (n, forms) => {
const idx = (n % 100 > 4 && n % 100 < 20)
? 2
: [2, 0, 1, 1, 1, 2][Math.min(n % 10, 5)];
return forms[idx];
};
const render = () => {
const diff = deadline - Date.now();
if (diff <= 0) {
box.textContent = 'Событие началось';
clearInterval(timer);
return;
}
const totalSec = Math.floor(diff / 1000);
const days = Math.floor(totalSec / 86400);
const hours = Math.floor((totalSec % 86400) / 3600);
const mins = Math.floor((totalSec % 3600) / 60);
const secs = totalSec % 60;
box.textContent =
`${days} ${plural(days, ['день', 'дня', 'дней'])} ` +
`${hours} ${plural(hours, ['час', 'часа', 'часов'])} ` +
`${mins} ${plural(mins, ['минута', 'минуты', 'минут'])} ` +
`${secs} ${plural(secs, ['секунда', 'секунды', 'секунд'])}`;
};
render();
const timer = setInterval(render, 1000);
}
Почему именно так
- Считаем от
Date.now(), а не накапливаем. Каждый тик берёт свежую разницу "дедлайн − сейчас", поэтому таймер не "плывёт", даже если вкладка была свёрнута или браузер притормозил интервалы. - Останавливаемся на нуле. При
diff <= 0вызываемclearInterval, иначе счётчик уйдёт в отрицательные значения. - Склонение по русским правилам. Функция
pluralвыбирает форму "день / дня / дней" по последней цифре и исключению для 11–14.
Подводные камни
- Часовой пояс. Unix-timestamp не зависит от пояса, но
MakeTimeStampтрактует строку свойства в поясе сайта. Если сервер и посетитель в разных поясах, договоритесь, что дедлайн задаётся во времени сайта, и не смешивайте это с локальным временем браузера. - Кэш компонента. Дата попадает в кэшируемый шаблон как timestamp – это правильно: кэшируется момент, а не "осталось столько-то". Сам отсчёт всегда живой, потому что считается на клиенте.
- Свойство не выводится. Если
DISPLAY_PROPERTIESпуст, значит свойство не отмечено в "Источнике данных" настроек компонента – проверьте настройки, это самая частая причина "пустого" таймера. - Одна дата – один блок. Скрипт написан под один
.event__counter. Для нескольких таймеров на странице оберните логику вquerySelectorAll(...).forEach(...), чтобы у каждого был свой дедлайн.
Тот же приём для баннера акции без инфоблока – в гайде "Скрипт отсчёта времени до конца акции". Про форматирование дат из свойств – "Сменить формат даты в CMS Битрикс" и "Формат Ydiff". Как вывести конкретное свойство элемента – "Как вывести определённое поле элемента инфоблока".
Частые вопросы
Как передать дату из свойства Битрикса в JavaScript-таймер?
Переведите значение свойства в Unix-timestamp через MakeTimeStamp, умножьте на 1000 и положите в data-атрибут блока. В JS заберите его через element.dataset и создайте счётчик – timestamp не зависит от формата даты и локали.
Нужен ли jQuery для счётчика обратного отсчёта?
Нет. В 2026 году таймер пишется на чистом JavaScript в несколько строк с setInterval и Date.now(). Отдельная библиотека вроде jQuery.countdown и сам jQuery ради одного счётчика избыточны.
Почему таймер показывает пустое значение?
Чаще всего свойство даты не отмечено в разделе "Источник данных" настроек компонента, поэтому оно не попадает в DISPLAY_PROPERTIES. Проверьте настройки news.list.
Как сделать правильное склонение "3 дня" / "5 дней"?
Используйте функцию plural, которая по последней цифре числа и исключению для 11–14 выбирает нужную форму из массива ['день','дня','дней'].
Почему счётчик уходит в минус после начала события?
Не остановлен интервал. Когда разница deadline − Date.now() становится
Материалы по теме
Что почитать дальше по этой теме