Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple modals overlay

Solution inspired by the answers of @YermoLamers & @Ketwaroo.

Backdrop z-index fix
This solution uses a setTimeout because the .modal-backdrop isn't created when the event show.bs.modal is triggered.

$(document).on('show.bs.modal', '.modal', function() {
  const zIndex = 1040 + 10 * $('.modal:visible').length;
  $(this).css('z-index', zIndex);
  setTimeout(() => $('.modal-backdrop').not('.modal-stack').css('z-index', zIndex - 1).addClass('modal-stack'));
});
  • This works for every .modal created on the page (even dynamic modals)
  • The backdrop instantly overlays the previous modal

Example jsfiddle

z-index
If you don't like the hardcoded z-index for any reason you can calculate the highest z-index on the page like this:

const zIndex = 10 +
  Math.max(...Array.from(document.querySelectorAll('*')).map((el) => +el.style.zIndex));

Scrollbar fix
If you have a modal on your page that exceeds the browser height, then you can't scroll in it when closing an second modal. To fix this add:

$(document).on('hidden.bs.modal', '.modal',
  () => $('.modal:visible').length && $(document.body).addClass('modal-open'));

Versions
This solution is tested with bootstrap 3.1.0 - 3.3.5


I realize an answer has been accepted, but I strongly suggest not hacking bootstrap to fix this.

You can pretty easily achieve the same effect by hooking the shown.bs.modal and hidden.bs.modal event handlers and adjusting the z-index there.

Here's a working example

A bit more info is available here.

This solution works automatically with arbitrarily deeply stacks modals.

The script source code:

$(document).ready(function() {

    $('.modal').on('hidden.bs.modal', function(event) {
        $(this).removeClass( 'fv-modal-stack' );
        $('body').data( 'fv_open_modals', $('body').data( 'fv_open_modals' ) - 1 );
    });

    $('.modal').on('shown.bs.modal', function (event) {
        // keep track of the number of open modals
        if ( typeof( $('body').data( 'fv_open_modals' ) ) == 'undefined' ) {
            $('body').data( 'fv_open_modals', 0 );
        }

        // if the z-index of this modal has been set, ignore.
        if ($(this).hasClass('fv-modal-stack')) {
            return;
        }

        $(this).addClass('fv-modal-stack');
        $('body').data('fv_open_modals', $('body').data('fv_open_modals' ) + 1 );
        $(this).css('z-index', 1040 + (10 * $('body').data('fv_open_modals' )));
        $('.modal-backdrop').not('.fv-modal-stack').css('z-index', 1039 + (10 * $('body').data('fv_open_modals')));
        $('.modal-backdrop').not('fv-modal-stack').addClass('fv-modal-stack'); 

    });        
});

Combining A1rPun's answer with the suggestion by StriplingWarrior, I came up with this:

$(document).on({
    'show.bs.modal': function () {
        var zIndex = 1040 + (10 * $('.modal:visible').length);
        $(this).css('z-index', zIndex);
        setTimeout(function() {
            $('.modal-backdrop').not('.modal-stack').css('z-index', zIndex - 1).addClass('modal-stack');
        }, 0);
    },
    'hidden.bs.modal': function() {
        if ($('.modal:visible').length > 0) {
            // restore the modal-open class to the body element, so that scrolling works
            // properly after de-stacking a modal.
            setTimeout(function() {
                $(document.body).addClass('modal-open');
            }, 0);
        }
    }
}, '.modal');

Works even for dynamic modals added after the fact, and removes the second-scrollbar issue. The most notable thing that I found this useful for was integrating forms inside modals with validation feedback from Bootbox alerts, since those use dynamic modals and thus require you to bind the event to document rather than to .modal, since that only attaches it to existing modals.

Fiddle here.


Something shorter version based off Yermo Lamers' suggestion, this seems to work alright. Even with basic animations like fade in/out and even crazy batman newspaper rotate. http://jsfiddle.net/ketwaroo/mXy3E/

$('.modal').on('show.bs.modal', function(event) {
    var idx = $('.modal:visible').length;
    $(this).css('z-index', 1040 + (10 * idx));
});
$('.modal').on('shown.bs.modal', function(event) {
    var idx = ($('.modal:visible').length) -1; // raise backdrop after animation.
    $('.modal-backdrop').not('.stacked').css('z-index', 1039 + (10 * idx));
    $('.modal-backdrop').not('.stacked').addClass('stacked');
});

I created a Bootstrap plugin that incorporates a lot of the ideas posted here.

Demo on Bootply: http://www.bootply.com/cObcYInvpq

Github: https://github.com/jhaygt/bootstrap-multimodal

It also addresses the issue with successive modals causing the backdrop to become darker and darker. This ensures that only one backdrop is visible at any given time:

if(modalIndex > 0)
    $('.modal-backdrop').not(':first').addClass('hidden');

The z-index of the visible backdrop is updated on both the show.bs.modal and hidden.bs.modal events:

$('.modal-backdrop:first').css('z-index', MultiModal.BASE_ZINDEX + (modalIndex * 20));