Методы setTimeout и setInterval: реализация таймера на JS и jQuery

В JavaScript существует возможность задавать способ исполнения кода не только синхронным образом, но и асинхронно, благодаря взаимодействию js с внутренним планировщиком среды выполнения кода. К примеру, нам необходимо запустить некоторый js-код через определенный промежуток времени разово либо постоянно с повторением через заданный интервал. Для реализации такой задержки в JavaScript существуют методы setTimeout и setInterval. В этой статье рассмотрим, как работают эти два метода, а также создадим таймер на js во всплывающем окне с использованием вышеуказанных методов на нативном js и с помощью библиотеки jQuery.

Таймер на JavaScript (jQuery): описание задачи и верстка

Прежде чем перейти к разбору методов setTimeout и setInterval, сперва опишем назначение создаваемого таймера и создадим заготовку на HTML и CSS.

Предположим, мы хотим реализовать на сайте рекламный блок с акцией на товар либо услугу. Этот блок будет содержать таймер, отсчитывающий время до конца акции. По завершении обратного отсчета будет появляться сообщение об истечении времени. Рекламный блок будет появляться через определенное время после открытия пользователем страницы.

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

<div class='demoTimerPopupWrapper' id='demoTimerPopupWrapper'>
    <article class='demoTimerPopup'>
        <h3 class='demoTimerPopup__title'>Акция!</h3>
        <div class='demoTimerPopup__text'>
            <p><span class='accent'>50% скидка</span> на подписку на все курсы по программированию!</p>
            <p class='center'>До конца осталось:</p>
        </div>
        <div class='demoTimerPopup__timer' id='demoTimerCounter'>
            <input type='text' id='demoTimerMinutes' readonly>
            <input type='text' id='demoTimerSeconds' readonly>
        </div>
        <div class='demoTimerPopup__expired' id='demoTimerExpired'>
            Акция завершена!            
        </div>
        <div class='demoTimerPopup__button'>
            <a href='#' id='demoTimerAction'>Успеть!</a>
        </div>
        <div class='demoTimerPopup__close' id='demoTimerClose'>x</div>
    </article>
</div>

Далее добавим CSS-стили:

.demoTimerPopupWrapper {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%!important;
    max-width: 100%!important;
    height: 100%!important;
    max-height: 100%!important;
    justify-content: center;
    align-items: center;
    background: rgba(204, 204, 204, 0.4);
    z-index: 99999;
    display: none;
}

.demoTimerPopup {
    background: #393939;
    box-shadow: 0 0 7px 2px #fff;
    width: 300px;
    max-width: 100%;
    max-height: 100%;
    padding: 26px 30px;
    font-size: 16px;
    position: relative;
}

.demoTimerPopup__title {
    color: #00c1c5;
    font-weight: bold;
    text-align: center;
    margin: 10px 0 10px;
}

.demoTimerPopup__text > p {
    margin-bottom: 8px!important;
}

.demoTimerPopup__timer {
    display: flex;
    justify-content: center;
    padding: 0 30%;
}

.demoTimerPopup__timer input[type="text"] {
    padding: 5px 10px;
    width: 44px;
    text-align: center;
    border: 2px solid #00c1c5;
    border-radius: 7px;
}

.demoTimerPopup__timer input[type="text"]:last-child {
    margin-left: 6px;
}

.demoTimerPopup__action {
    display: flex;
    justify-content: center;
    margin-top: 15px;
}

.demoTimerPopup__action a {
    display: inline-block;
    padding: 5px 10px;
    border-radius: 7px;
    background: #00c1c5;
    color: #fff!important;
    text-decoration: none;
    font-weight: bold;
    transition: 0.3s;
}

.demoTimerPopup__action a:hover {
    background: #fff!important;
    color: #00c1c5!important;
}

.demoTimerPopup__expired {
    text-align: center;
    margin-top: 15px;
    display: none;
}

.demoTimerPopup__close {
    width: 24px;
    height: 24px;
    color: #00c1c5;
    cursor: pointer;
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    top: 0;
    right: 0;
}

.accent {
    color: #00c1c5;
    font-weight: bold;
}

.center {
    text-align: center;
}

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

Акция с таймером

Теперь мы готовы перейти к рассмотрению первого метода — setTimeout.

Метод setTimеout

Этот метод предназначен для того, чтобы вызвать JavaScript-код один раз через промежуток времени. Синтаксис метода выглядит следующим образом:

const timeoutId = setTimeout(func, delay, param1,  …, paramN);
  • func — функция, вызываемая по истечении установленного времени delay.
  • delay — время, по истечении которого вызывается функция func. Устанавливается в миллисекундах. Например, 7000 — 7 секунд (или 7000 миллисекунд).
  • param1, …, paramN — список параметров, передаваемых в функцию func.

Метод возвращает идентификатор таймаута, который можно использовать для отмены запуска функции func.

Вместо функции func, можно передавать строку с кодом, но этот вариант не рекомендуется из соображений безопасности.

setTimeout используем в нашем примере для появления всплывающего окна с рекламным блоком через 3 секунды. Для этого добавим этот метод в следующем виде:

setTimeout(function() {
    // Рекламный блок изначально скрыт с помощью CSS-свойства "display" со значением "none".
    // Изменим значение "display" на "flex"
    document.getElementById('demoTimerPopupWrapper').style.display = 'flex';
}, 3000);

Для живой демонстрации примера повесим обработчик js-кода на событие onclick демокнопки с идентификатором startDemoTimer. Нажав на нее и подождав три секунды, можно увидеть текущий результат — появится окно с рекламным блоком.

Акция!

50% скидка на подписку на все курсы по программированию!

До конца осталось:

Акция завершена!
x

Есть ли возможность отменить запуск кода функции setTimeout после начала отсчета времени до запуска кода?

Метод clearTimеout

Как упоминалось выше, setTimeout возвращает идентификатор. Передав этот идентификатор в метод clearTimeout, мы отменим запуск функции, которая была передана методу setTimeout.

// Запустим код с задержкой и получим идентификатор
const timeoutId = setTimeout(function() {
    document.getElementById('demoTimerPopupWrapper').style.display = 'flex';
}, 3000);

// Отменим выполнения кода, переданного в setTimeout, нажатием на кнопку с идентификатором cancelTimeout
// Это нужно сделать в течение трех секунд после нажатия на кнопку "Старт"
document.getElementById('cancelTimeout').addEventListener('click', function() {
    clearTimeout(timeoutId);
});

Для нашего примера этот функционал не нужен, но для демонстрации создадим еще одну кнопку, нажав на которую в течение трех секунд после нажатия на кнопку запуска, мы увидим, что всплывающее окно не сможет отобразиться.

Акция!

50% скидка на подписку на все курсы по программированию!

До конца осталось:

Акция завершена!
x

Теперь перейдем к созданию самого таймера обратного отсчета.

Метод setInterval

Метод setInterval имеет идентичный методу setTimeout синтаксис и принимает такие же параметры. Главное отличие в том, что setInterval выполняет переданную ему функцию периодически через установленный интервал времени.

const timerId = setInterval(func, delay, param1,  …, paramN);

После того как всплывающее окно появится, на нем будет работать таймер с обратным отсчетом. Метод setInterval для такой реализации подходит идеально — каждую секунду вызывается функция, которая уменьшает текущее значение в поле для секунд на единицу, а число в поле для минут таймера будет также уменьшаться на единицу только при условии, когда секунды дошли до нуля.

Для нашего примера сделаем количество минут акции равным пятнадцати. Обновленный js-код будет выглядеть следующим образом:

// Для демонстрации запуск выполнения кода повесим на событие нажатия кнопки
document.getElementById('startDemoTimer').addEventListener('click', function () {
    const that = this;
    // Заблокируем кнопку и изменим текст на ней до появления окна
    that.textContent = "Ждите...";
    that.setAttribute('disabled', '');
    setTimeout(function() {
        document.getElementById('demoTimerPopupWrapper').style.display = 'flex';
        startDemoTimer(15, 0);
        // Вернем кнопке начальное состояние
        that.textContent = "Старт";
        that.removeAttribute('disabled');
    }, 3000);
});

// Функция счетчика
function startDemoTimer(minutes, seconds) {
    // Функция setValueToTimerField будет устанавливать значения минут или секунд в соответствующие поля
    setValueToTimerField('demoTimerMinutes', minutes);
    setValueToTimerField('demoTimerSeconds', seconds);

    // Запустим счетчик и получим его идентификатор
    // Функция countDemoTime будет выполняться каждую секунду
    const counterId = setInterval(countDemoTime, 1000);

    // Сохраним id счетчика в качестве значения data-атрибута в HTML-элементе с идентификатором demoTimerCounter
    // Это нужно, чтобы в последствии иметь возможность остановить счетчик
    document.getElementById('demoTimerCounter').dataset.counterId = String(counterId);
}

// Функция, вызываемая каждую секунду
function countDemoTime() {
    // Возьмем текущие значения минут и секунд
    let currentMinutes = document.getElementById('demoTimerMinutes').value;
    let currentSeconds = document.getElementById('demoTimerSeconds').value;

    // Если секунды равны нулю, уменьшаем значение минут на единицу, а значение секунд устанавливаем в 59
    // Это означает, что начинается отсчет следующей минуты в обратном порядке
    if (+currentSeconds === 0) {
        setValueToTimerField('demoTimerMinutes', --currentMinutes);
        setValueToTimerField('demoTimerSeconds', 59);
    } else {
        setValueToTimerField('demoTimerSeconds', --currentSeconds);
    }
}

Однако мы не можем позволить таймеру работать бесконечно. В момент, когда поля минут и секунд станут равными нулям, мы остановим таймер и выведем сообщение о том, что время акции истекло. Такую возможность реализуем с помощью метода clearInterval.

Метод clearInterval

Метод cleaInterval работает подобно методу clearTimeout: останавливает таймер по переданному идентификатору:

Дополним наш код, остановив таймер, когда он обнулится. После этого выведем сообщение. Наш итоговый код будет выглядеть следующим образом:

// Для демонстрации запуск выполнения кода повесим на событие нажатия кнопки
document.getElementById('startDemoTimer').addEventListener('click', function () {
    const that = this;
    // Заблокируем кнопку и изменим текст на ней до появления окна
    that.textContent = "Ждите...";
    that.setAttribute('disabled', '');
    setTimeout(function() {
        document.getElementById('demoTimerPopupWrapper').style.display = 'flex';
        startDemoTimer(15, 0);
        // Вернем кнопке начальное состояние
        that.textContent = "Старт";
        that.removeAttribute('disabled');
    }, 3000);
});

// Функция счетчика
function startDemoTimer(minutes, seconds) {
    // Функция setValueToTimerField будет устанавливать значения минут или секунд в соответствующие поля
    setValueToTimerField('demoTimerMinutes', minutes);
    setValueToTimerField('demoTimerSeconds', seconds);

    // Запустим счетчик и получим его идентификатор
    // Функция countDemoTime будет выполняться каждую секунду
    const counterId = setInterval(countDemoTime, 1000);

    // Сохраним id счетчика в качестве значения data-атрибута в HTML-элементе с идентификатором demoTimerCounter
    // Это нужно, чтобы в последствии иметь возможность остановить счетчик
    document.getElementById('demoTimerCounter').dataset.counterId = String(counterId);
}

// Вспомогательная функция для установки значений минут и секунд
function setValueToTimerField(fieldId, value) {
    document.getElementById(fieldId).value = String(value).padStart(2, '0');
}

// Функция, вызываемая каждую секунду
function countDemoTime() {
    // Возьмем текущие значения минут и секунд
    let currentMinutes = document.getElementById('demoTimerMinutes').value;
    let currentSeconds = document.getElementById('demoTimerSeconds').value;

    // Если текущие значения минут и секунд равны нулям...
    if ((+currentMinutes === 0) && (+currentSeconds === 0)) {
        // ...заберем сохраненный id счетчика у элемента demoTimerCounter и передадим этот идентификатор функции clearInterval для остановки счетчика...
        clearInterval(+document.getElementById('demoTimerCounter').dataset.counterId);
        // ...отобразим сообщение об окончании акции...
        document.getElementById('demoTimerExpired').style.display = 'block';
        // ...уберем кнопку для перехода на страницу с акцией...
        document.getElementById('demoTimerAction').style.display = 'none';
        // ...закончим на этом работу счетчика
        return;
    }

    // Если секунды равны нулю, уменьшаем значение минут на единицу, а значение секунд устанавливаем в 59
    // Это означает, что начинается отсчет следующей минуты в обратном порядке
    if (+currentSeconds === 0) {
        setValueToTimerField('demoTimerMinutes', --currentMinutes);
        setValueToTimerField('demoTimerSeconds', 59);
    } else {
        setValueToTimerField('demoTimerSeconds', --currentSeconds);
    }
}

// Остановим счетчик при нажатии на кнопку "Успеть"
document.getElementById('demoTimerAction').addEventListener('click', function(event) {
    event.preventDefault();
    clearInterval(+document.getElementById('demoTimerCounter').dataset.counterId);
});

// При нажатии на крестик с правом верхнем углу блока, спрячем этот блок и остановим счетчик, чтобы при следующем открытии всплывающего окна не работало несколько таймеров
document.getElementById('demoTimerClose').addEventListener('click', function() {
    document.getElementById('demoTimerPopupWrapper').style.display = 'none';
    clearInterval(+document.getElementById('demoTimerCounter').dataset.counterId);
});

Этот js-код можно переписать, используя возможности библиотеки jQuery:

jQuery(function($) {
    $('#startDemoTimer').click(function() {
        const that = $(this);
        that.text("Ждите...");
        that.attr('disabled', true);
        setTimeout(function() {
            $('#demoTimerPopupWrapper').css('display', 'flex');
            startDemoTimer(15, 0);
            that.text("Старт");
            that.attr('disabled', false);
        }, 3000);
    });

    function startDemoTimer(minutes, seconds) {
        setValueToTimerField('demoTimerMinutes', minutes);
        setValueToTimerField('demoTimerSeconds', seconds);

        const counterId = setInterval(countDemoTime, 1000);
        $('#demoTimerCounter').attr('data-counter-id', String(counterId));
    }

    function setValueToTimerField(fieldId, value) {
        $('#' + fieldId).val(String(value).padStart(2, '0'));
    }

    function countDemoTime() {
        let currentMinutes = $('#demoTimerMinutes').val();
        let currentSeconds = $('#demoTimerSeconds').val();

        if ((+currentMinutes === 0) && (+currentSeconds === 0)) {
            clearInterval(+$('#demoTimerCounter').attr('data-counter-id'));
            $('#demoTimerExpired').css('display', 'block');
            $('#demoTimerAction').css('display', 'none');
            return;
        }

        if (+currentSeconds === 0) {
            setValueToTimerField('demoTimerMinutes', --currentMinutes);
            setValueToTimerField('demoTimerSeconds', 59);
        } else {
            setValueToTimerField('demoTimerSeconds', --currentSeconds);
        }
    }

    $('#demoTimerAction').click(function(event) {
        event.preventDefault();
        clearInterval(+$('#demoTimerCounter').attr('data-counter-id'));
    });

    $('#demoTimerClose').click(function() {
        $('#demoTimerPopupWrapper').css('display', 'none');
        clearInterval(+$('#demoTimerCounter').attr('data-counter-id'));
    });
});

В итоге у нас получился ожидаемый рекламный блок. Результат можно увидеть, нажав на кнопку «Старт»:

Акция!

50% скидка на подписку на все курсы по программированию!

До конца осталось:

Акция завершена!
x

Мы запускаем наше всплывающее окно при нажатии на кнопку с помощью JavaScript-события click. Изначально мы предположили, что этот блок будет появляться после загрузки страницы. Примеры, как этого достичь на нативном JavaScript и с помощью библиотеки jQuery, ниже :

// JavaScript
document.addEventListener('DOMContentLoaded', function () {
    setTimeout(function() {
        document.getElementById('demoTimerPopupWrapper').style.display = 'flex';
        startDemoTimer(15, 0);
    }, 3000);
});

// jQuery
$(document).ready(function() {
    setTimeout(function() {
        $('#demoTimerPopupWrapper').css('display', 'flex');
        startDemoTimer(15, 0);
    }, 3000);
});

После такого изменения блок отобразится через три секунды загрузки страницы.

Заготовка нашего таймера готова. Мы рассмотрели работу методов setTimeout и setInterval на реальном примере. Получилось ли у вас создать таймер на js? Поделитесь в комментариях.

Оцените статью
DevReflex
Добавить комментарий