Баннер акции с таймером «до конца распродажи осталось 2 дня 05:14:30» подгоняет к покупке и не требует ни jQuery, ни внешних библиотек. Достаточно одного блока разметки и десятка строк на современном JavaScript. Разберём рабочий вариант и починим типичные ошибки старых сниппетов.
Разметка: где хранить дату окончания
Дату конца акции удобно держать прямо в разметке – в data-атрибуте блока. Формат ISO 8601 с указанием времени и часового пояса разбирается всеми браузерами одинаково, поэтому лучше писать полную дату, а не короткую 2026-05-31:
<div id="deal-countdown" class="countdown"
data-deadline="2026-05-31T23:59:59+03:00"></div>
Почему это важно: строку "2026-05-31" (только дата) стандарт велит трактовать как полночь UTC, а "2026-05-31 23:59:59" (с пробелом вместо T) старые версии Safari вообще отказывались разбирать и возвращали Invalid Date. Полный ISO-формат с поясом снимает обе проблемы.
Скрипт таймера на чистом JavaScript
Забираем дедлайн из атрибута, раз в секунду считаем разницу с текущим моментом и обновляем текст. Современный синтаксис (const/let, стрелочные функции, шаблонные строки) делает код короче и понятнее старого варианта на var:
const box = document.getElementById('deal-countdown');
if (box) {
const deadline = new Date(box.dataset.deadline).getTime();
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 pad = (n) => String(n).padStart(2, '0');
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, ['день', 'дня', 'дней'])} ` +
`${pad(hours)}:${pad(mins)}:${pad(secs)}`;
};
render(); // сразу, без задержки в секунду
const timer = setInterval(render, 1000);
}
Что здесь исправлено по сравнению со старым сниппетом
- Отрицательные значения. Старый код продолжал считать после окончания акции и показывал минусы. Теперь при
diff <= 0выводится «Акция завершена» и интервал останавливается черезclearInterval. - Мгновенный старт. Прежний таймер целую секунду висел пустым, пока не сработает первый
setInterval. Мы вызываемrender()сразу. - Округление.
parseIntзаменён наMath.floor– это семантически правильнее для отбрасывания дробной части и не зависит от строкового приведения. - Русские подписи и склонение. Вместо «Days/Hours» – «день/дня/дней» с корректной формой, а часы/минуты/секунды выводятся в привычном формате
чч:мм:ссс ведущими нулями (padStart). - Проверка наличия блока.
if (box)защищает от ошибки на страницах, где баннера нет.
Почему счёт всегда от Date.now()
Каждый тик берёт свежую разницу «дедлайн минус текущий момент», а не вычитает секунду из прошлого значения. Поэтому таймер не накапливает ошибку и остаётся точным, даже если вкладку свернули, а браузер придержал таймеры для экономии батареи – вернувшись, пользователь увидит верное время.
Подводные камни
- Часовой пояс дедлайна. Указывайте пояс прямо в дате (
+03:00). Иначе «полночь» для клиента в Калининграде и во Владивостоке наступит в разные моменты, и акция закончится у всех по-разному. - Клиентские часы врут. Таймер опирается на время устройства посетителя. Если критична точность до секунды (аукцион, флеш-распродажа), сверяйтесь с серверным временем, а не только с
Date.now(). - Несколько таймеров. Скрипт написан под один блок по
id. Для нескольких акций используйте класс иquerySelectorAll(...).forEach(...), читая дедлайн изdata-атрибута каждого. - Кэш страницы. Дедлайн в разметке кэшируется вместе со страницей – это нормально: сам отсчёт живой, потому что считается на клиенте. Менять дату акции нужно в исходнике, а не рассчитывать её на сервере при каждом заходе.
Вариант этого таймера, где дата берётся из свойства элемента инфоблока, – в гайде «Счётчик обратного отсчёта в списке новостей». Про работу с датами на стороне сервера – «Сменить формат даты в CMS Битрикс», «Формат Ydiff» и «Вывод текущего года на PHP».