Люблю, когда в коде нет магии, а есть понятная логика: "вот почему так, вот откуда взялось". Для меня ИИ — не про "дай готовый код", а про "давай разберём, где тут лишнее и что реально нужно".
Продолжаю возиться с таймером, вышло как у многих: на коленке набросанные 10 строк, потом тестирование в поле подкинуло кейсов (вкладки в фоне, кеш, выгрузка браузера), и код распух до 136 строк, превратившись в кашу из костылей. Нужно было оптимизировать и доработать. Так что с помощью ИИ из кода вытащил логику работы (алгоритм) и все это дело переписал, видя всю картину и уложившись в 39 строк с сохранением ресурсов памяти, процессорного времени и без потери функционала.
Как это устроено:
Старт. При загрузке один раз читаем localStorage. Если cardId не совпадает или данных нет — прописываем новый старт, иначе продолжаем отсчет с сохранённого времени.
Тик. Каждую секунду считаем остаток от startTime и выводим его в <span>. К localStorage больше не лезем.
Пауза по вкладке. На visibilitychange ставим интервал на паузу, startTime не трогаем — при возврате честно досчитываем остаток секунд.
Конец таймера. Останавливаем интервал, чистим localStorage, прячем карту, показываем сообщение и кнопку для показа следующей.
Кнопка. Сбрасывает localStorage и перезагружает страницу — таймер стартует заново.
Защита. Если соле для счетчика #countdown span не найдено — скрипт не падает, просто не обновляет цифру.
Итоговый скрипт (39 строк)
<button id="refresh-card-btn" data-card-id="12345">Получить новую карту</button>
<h1 id="countdown"><span>120</span> сек. до загрузки другой карты</h1>
<div id="qrcode"></div>
<h2 id="сr_code"></h2>
<script type="text/javascript">
const DURATION = 120000;
const LS_START = 'timer_start_ms';
const LS_CARD = 'current_card_id';
let timer = null;
let startTime = null;
const btn = document.getElementById('refresh-card-btn');
const cardId = btn?.dataset?.cardId || '';
{
const savedStart = localStorage.getItem(LS_START);
const savedCard = localStorage.getItem(LS_CARD);
if (!savedStart || savedCard !== cardId) {
startTime = Date.now();
localStorage.setItem(LS_START, String(startTime));
localStorage.setItem(LS_CARD, cardId);
} else startTime = parseInt(savedStart, 10); }
function tick() {
const left = Math.max(0, DURATION - (Date.now() - startTime));
const span = document.querySelector('#countdown span');
if (span) span.textContent = Math.ceil(left / 1000);
if (left <= 0) {
clearInterval(timer);
timer = null;
localStorage.removeItem(LS_START);
localStorage.removeItem(LS_CARD);
const qr = document.getElementById('qrcode');
if (qr) qr.innerHTML = '';
const h1 = document.getElementById('countdown');
if (h1) h1.innerHTML = 'Для получения новой карты обновите страницу или нажмите кнопку “Получить новую карту”';
return; } }
function start() {if (timer) return; timer = setInterval(tick, 1000); tick();}
document.addEventListener('visibilitychange', () => {document.hidden ? clearInterval(timer, timer = null) : start();});
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start);
else start();
btn?.addEventListener('click', () => {
localStorage.removeItem(LS_START);
localStorage.removeItem(LS_CARD);
window.location.reload(); });
</script>