Отображение процесса AJAX запроса при помощи модального окна

Часто при использовании AJAX зыдумываюсь о том, как было бы круто уведомлять пользователей о стадии запроса. Что бы всё это было автоматически, но в итоге каждый раз обходился парой костылейю...

Для начала создадим само модальное окно с дополнительными примочками:

<div class="modal fade" id="processing-modal" tabindex="-1" role="dialog" data-backdrop="static" data-keyboard="false" style="z-index: 1000000000">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header" style="border:0">
                <h4 class="modal-title text-center">Processing...</h4>
            </div>
            <div class="modal-body"></div>
        </div>
    </div>
</div>

  • data-backdrop="static" и data-keyboard="false" предотвращает закрытие окна по клику
  • style="z-index: 1000000000" выводит модальное окно поверх всех существующих слоёв

Далее добавим в div.modal-body значок выполнения действия, а так же прогрессбар, который будет вылезать при > 1 запросах давая понять пользователю что система не зависла, а всё ещё в процессе.

<div class="text-center" style="padding-bottom:15px">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
</div>
<div class="progress" style="margin: 10px 0; display:none">
    <div class="progress-bar progress-bar-striped active" role="progressbar"></div>
</div>

Теперь самое время обратится к документации jQuery, а именно нам понадобится ajaxSetup и его методы:

  • beforeSend - выстреливает перед отправкой запроса
  • complete - выстреливает после получения запроса
  • error - выстреивает при ошибке запроса

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

var processing_modal = jQuery('#processing-modal');

function isNumber(number) {
    return !isNaN(parseFloat(number)) && isFinite(number);
}

jQuery.ajaxSetup({
    // Выполнение перед запросом
    'beforeSend': function() {
        // Создаём счётчик если он не существует
        if (isNumber(window.ajax_counter) == false)
            window.ajax_counter = 0;

        // Показываем модальное окно если оно спрятано
        if (window.ajax_counter == 0)
            processing_modal.modal('show');
        
        // Увеличиваем счётчик
        window.ajax_counter++;
        
        // Если запросов больше одного то показывае прогрессбар
        if (window.ajax_counter > 1) {
            var pb = processing_modal.find('.progress').show();
            
            // Обнуляем прогрессбар в начале запросов
            if (window.ajax_counter == 2)
                pb.find('.progress-bar').css('width', '0');
        }
    },
    // Выполнение после запроса
    'complete': function(response) {
        // Создаём счётчик максимального количества запросов
        // Он нужен для того, что бы знать количество запросов для подсчёта прогресса
        if (isNumber(window.ajax_max) == false)
            window.ajax_max = 0;
        
        // Увеличиваем если появляется новый запрос
        if (window.ajax_counter > window.ajax_max)
            window.ajax_max = window.ajax_counter;

        // Уменьшаем количество запросов
        window.ajax_counter--;
        
        // Высчитываем прогресс
        var progress = 100 - (100 / window.ajax_max) * window.ajax_counter;
        
        // Устанавливаем прогресс
        processing_modal.find('.progress').show().find('.progress-bar').css('width', progress + '%');
        
        // Прячем окно если все запросы выполнены
        if (window.ajax_counter == 0) {
            processing_modal.modal('hide');
            processing_modal.find('.progress').hide().find('.progress-bar').css('width', '0');
            
            window.ajax_max = 0;
        }
    },
    // В случае какой-либо ошибки выводим сообщение и прячем окно
    'error': function(xmlHttpRequest, textStatus, errorThrown) {
        alert('A critical error has occured. Please reload the page and try again.');
        
        processing_modal.modal('hide');
    },
});
Fork me on GitHub