Better Bootstrap Modals

It's 2016, we need better modals.

View on GitHubDownload v0.4.1.min.tar.gzDownload v0.4.1.tar.gz

Problem solved

Simple. Tiny.

This simple and tiny (1088 bytes gzipped) Javascript and CSS package allows an infinite amount of open modals, and a simple and natural way to navigate between them.

Utilizing Bootstrap's own modal events, this works neatly with the core of vanilla Bootstrap.

Note: on mobile devices, the body may have a tendency to scroll when at the top or bottom of the .modal-container. This is a well-known issue.

Example

Try clicking this text, or the buttons below, to open an example modal.

Button link

If you want to try some of the new options, you can toggle them right here. (See 'Options' section further down for more info.)

Requirements

For Better Bootstrap Modals to work, all you need to do is include the CSS and Javascript files (or include the tiny source code found below), and be sure to not include tabindex="-1" on any modals. (It automatically adds the tabindex to the most recently opened modal.)

It of course also requires Bootstrap and jQuery.

Don't include tabindex="-1"

<!-- Basic Bootstrap modal without tabindex -->
<div class="modal" id="modal1" role="dialog">
...
</div>

Options

As of v0.4.0, you can specify a few options. Options can be passed via data attributes by appending the option name to data-, as in data-modal-show="", on the <script> tag used to reference the Better Bootstrap Modals file. Example: <script src="..." data-*=""></script>

Name type default description
modal-show the string 'first' false Only shows one modal at a time, the most recently opened modal being shown.
force-backdrop boolean false Better Bootstrap Modals uses a custom backdrop, which cannot be disabled. However, this option forces the default built-in backdrop, adding a layer for every modal you open. (Not recommended.)

Source code (v0.4.1)

To demonstrate how tiny Better Bootstrap Modals really is, here's all the source code (uncompressed):

CSS

.modal-container {
-webkit-overflow-scrolling: touch;
overflow-y: scroll;
z-index: 1041;

position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;

background: rgba(0, 0, 0, 0.24);
}

.modal-container.backdrop {
background: transparent;
}

.modal-container .modal {
position: relative;
}

body.modal-open .modal-container .modal.in {
display: block;
}

body.modal-open .modal-container.show-first-only .modal.in ~ .modal.in {
display: none;
}

Javascript

+function() {
var bbm_modals = [],
data_vars = (function() { var scripts = document.getElementsByTagName("script"); return scripts[scripts.length - 1].dataset; }()),
ti = "tabindex",
cont = "modal-container";

if (data_vars.forceBackdrop === "true") cont += " backdrop";
else $.fn.modal.Constructor.DEFAULTS.backdrop = 0;

if (data_vars.modalShow === "first") cont += " show-first-only";
var $cont = $('<div class="' + cont + '"></div>');

$(document).ready(function() {
$(".modal").on("show.bs.modal", function() {
var self = $(this);

if (bbm_modals.length == 0) $("body").prepend($cont);
$cont.prepend(self);

bbm_modals.push(self.attr("id"));
$cont.find(".modal.in").removeAttr(ti);
self.show().attr(ti, "-1");
});

$(".modal").on("shown.bs.modal", function() {
$(this).css("display", "");
});

$(".modal").on("hide.bs.modal", function() {
var self = $(this);

bbm_modals.splice(bbm_modals.indexOf(self.attr("id")), 1);
self.hide();

$("body").prepend(self);

if (bbm_modals.length == 0) $cont.detach();
});

$(".modal").on("hidden.bs.modal", function() {
$(this).removeAttr(ti);

if (bbm_modals.length > 0) {
$("body").addClass("modal-open");
$cont.find(".modal.in").eq(0).attr(ti, "-1");
}
});

$("body").on("click", "." + $cont[0].classList[0], function(e) {
if (!$(e.target).is(".modal-dialog *")) $(".modal").modal("hide");
});
});
}();