Skip to content

Instantly share code, notes, and snippets.

@larrybahr
Created September 20, 2018 01:27
Show Gist options
  • Save larrybahr/91d852be5c22743af3f9b561c681c01f to your computer and use it in GitHub Desktop.
Save larrybahr/91d852be5c22743af3f9b561c681c01f to your computer and use it in GitHub Desktop.
Bootstrap 4 Loading Modal

Bootstrap 4 Loading Modal (Live Demo)

Display any number of loading messages one at a time

Requirements

Features

  • Display non blocking loading dialog with customizable messages
  • Modals are queued and shown one at a time

Methods

Show

 /**
 * @description Displays the modal
 * @param {object} options
 * @param {string} [options.message] - Message to display in the modal
 * @return {string} Modal ID
 */
 LoadingModal.Show(options: object): string

Hide

 /**
 * @description Hides the modal
 * @param {object} options
 * @param {string} options.id - Modal ID
 * @return {void}
 */
LoadingModal.Hide(options: object): void

Example

Live Demo

var modalIdOne;
var modalIdTwo;

modalIdOne = LoadingModal.Show({
  message: "Running test 1 for 3 seconds..."
});

modalIdTwo = LoadingModal.Show({
  message: "Running test 2 for 2 more seconds..."
});

setTimeout(function () {
  LoadingModal.Hide({
    id: modalIdOne
  });
  return;
}, 3000);

setTimeout(function () {
  LoadingModal.Hide({
    id: modalIdTwo
  });
  return;
}, 5000);

License

MIT

/**
* @description Creates a modal that can be displayed while things are loading
*/
(function ()
{
"use strict";
var loadingModal = {};
var queue = {};
var currentQueueIndex = 0;
var isShown = false;
var modalStateChangingPromise = Promise.resolve(); /** IE 11 breaks if we try to do anything with the modal before Bootstrap is done chaning its state */
var modal = $(
'<div class="modal fade" aria-hidden="true" role="dialog">' +
' <div class="modal-dialog modal-sm modal-dialog-centered" role="document">' +
' <div class="modal-content">' +
' <div class="modal-body">' +
' <h5 class="modal-title text-muted text-center" style="word-wrap: break-word;"></h5>' +
' <p class="text-center text-primary mt-1">' +
' <i class="fas fa-cog fa-pulse fa-5x"></i>' +
' </p>' +
' </div>' +
' </div>' +
' </div>' +
'</div>'
);
var UpdateModal = function ()
{
return modalStateChangingPromise
.then(function ()
{
var queueIndexes = Object.keys(queue);
var options;
/**
* If there is nothing left to display then exit.
*/
if (0 === queueIndexes.length)
{
/**
* If the modal is still showing then hide it before we exit
*/
if (true === isShown)
{
isShown = false;
modalStateChangingPromise = modalStateChangingPromise
.then(function ()
{
return new Promise(function (resolve)
{
modal.on('hidden.bs.modal', function ()
{
/**
* Clean up the DOM
*/
modal.off('hidden.bs.modal');
modal.detach();
resolve();
return;
});
modal.modal('hide');
return;
});
});
}
return;
}
/**
* If the modal is not in the DOM then add it (the DOM gets cleaned when it is hidden)
*/
if (false === $.contains(document.documentElement, modal[0]))
{
$("body").append(modal);
}
/**
* Get the options for the first thing on the queue.
* Update the modal with those options.
*/
options = queue[queueIndexes[0]];
if ("string" !== typeof (options.message))
{
options.message = "Loading...";
}
modal.find(".modal-title").html(options.message);
/**
* Display the modal if it is hidden
*/
if (false === isShown)
{
isShown = true;
modalStateChangingPromise = modalStateChangingPromise
.then(function ()
{
return new Promise(function (resolve)
{
modal.on('shown.bs.modal', function ()
{
modal.off('shown.bs.modal');
resolve();
return;
})
modal.modal({
keyboard: false,
backdrop: 'static',
focus: true,
show: true
});
return;
});
});
}
return;
});
};
/**
* @description Displays the modal
* @param {object} options
* @param {string} [options.message] - Message to display in the modal
* @return {string} Modal ID
*/
loadingModal.Show = function (options)
{
var modalId;
/**
* Param check
*/
if ("object" !== typeof options ||
null === options)
{
options = {};
}
/**
* Save the current queue index and increment to the next queue index
*/
modalId = currentQueueIndex + "";
currentQueueIndex++;
queue[modalId] = options;
/**
* The user will see a flash on the screen if the modal is displayed for only a brief time.
* This will give the process a chance to finish before displaying it.
* 300 milliseconds is when a user will feel like a process is not instant and there is a delay, therefore we need a dialog
* Also showing a loading dialog can make a user feel like things are slower than they really are
* https://medium.com/@luisvieira_gmr/understanding-the-critical-rendering-path-rendering-pages-in-1-second-735c6e45b47a
*/
setTimeout(function ()
{
UpdateModal();
return;
}, 300);
return modalId;
}
/**
* @description Hides the modal
* @param {object} options
* @param {string} options.id - Modal ID
* @return {void}
*/
loadingModal.Hide = function (options)
{
if ("object" !== typeof options ||
null === options)
{
return;
}
if ("string" !== typeof options.id)
{
return;
}
if ("object" === typeof queue[options.id])
{
/**
* Remove this from the queue so it is not shown
*/
delete queue[options.id];
}
UpdateModal();
return;
}
/**
* Export this so it can be used in the global space
*/
window.LoadingModal = loadingModal;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment