Часто при использовании 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');
	},
});