问题现象如下:

各位看官可能已经看出了问题,页面先后弹出 3 个 modal,看效果 modal 显示正确,但是遮罩都堆在第 1 个 modal 下面了,期望效果是 modal3 遮罩盖住 modal2,modal2 遮罩盖住 modal1。
可以在 modal 显示的时候,将 modal 和遮罩设置合适的 z-index 属性,让最后打开的 modal 和遮罩显示在最上面。
具体方案是:重写 bootstrap.js 中 modal 的 show 方法,取当前所有显示状态的 modal 和遮罩, 计算所有这些元素的 z-index 属性最大值 max, 将即将显示的遮罩 z-index 属性设置为 max+10, 将即将显示的 modal 的 z-index 属性设置为 max+20
先创建实例代码,从 bootstrap 官网 copy 的代码基本结构,增加 3 个 modal
<!-- Modal -->
<div class="modal fade" id="myModal" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">1</h4>
</div>
<div class="modal-body">
我是 1
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal1">
打开 2
</button>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="myModal1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" >2</h4>
</div>
<div class="modal-body">
我是 2
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal2">
打开 3
</button>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="myModal2" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog modal-sm" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" >3</h4>
</div>
<div class="modal-body">
我是 3
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
分析 bootstrap 源码,版本:3.4.1;找到 modal 的 show 方法:

看源码是通过 Modal 对象设置的,往上看源码发现 Modal 是一个构造函数似得存在:

并且最终作为 modal 的构造函数:

分析到这里,我们就确定:重写 modal 构造器里的 show 方法
我们在示例程序最下面添加 js 代码,先将 Modal 取出来:
<script type="text/javascript">
+function ($) {
let Modal = $.fn.modal.Constructor; // 先将 Modal 取出来
}(jQuery);
</script>
然后将 bootstrap.js 中的 show 方法源码完整的拷贝到示例中:

拷贝完之后找到设置 backdrop 属性的匿名函数:

在匿名函数里面加上扩展代码:

// expansion part
// 取当前所有显示状态的 modal 和遮罩, 计算所有这些元素的 z-index 属性最大值 max, 将即将显示的遮罩 z-index 属性设置为 max+10, 将即将显示的 modal 的 z-index 属性设置为 max+20
let $beShowDialog = that.$element;
let $beShowBackDrop = that.$backdrop;
let zIndexAry = $.map($(".modal:visible,.modal-backdrop:visible"), function(obj){return $(obj).css('z-index')});
let max = 1030;
if (zIndexAry && zIndexAry.length > 0) {
max = Math.max.apply(null, zIndexAry);
}
$beShowDialog.css("z-index", max + 20);
$beShowBackDrop.css("z-index", max + 10);
最终效果:

核心代码:
// 修复模态框多个不能被覆盖的 bug
+(function ($) {
let Modal = $.fn.modal.Constructor;
Modal.prototype.show = function (_relatedTarget) {
// the source code copy from bootstrap.js, version = 3.4.1
var that = this;
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget });
this.$element.trigger(e);
if (this.isShown || e.isDefaultPrevented()) return;
this.isShown = true;
this.checkScrollbar();
this.setScrollbar();
this.$body.addClass('modal-open');
this.escape();
this.resize();
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this));
this.$dialog.on('mousedown.dismiss.bs.modal', function () {
that.$element.one('mouseup.dismiss.bs.modal', function (e) {
if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true;
});
});
this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade');
if (!that.$element.parent().length) {
that.$element.appendTo(that.$body); // don't move modals dom position
}
that.$element.show().scrollTop(0);
that.adjustDialog();
if (transition) {
that.$element[0].offsetWidth; // force reflow
}
that.$element.addClass('in');
that.enforceFocus();
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget });
transition
? that.$dialog // wait for modal to slide in
.one('bsTransitionEnd', function () {
that.$element.trigger('focus').trigger(e);
})
.emulateTransitionEnd(Modal.TRANSITION_DURATION)
: that.$element.trigger('focus').trigger(e);
// expansion part
// 取当前所有显示状态的 modal 和遮罩, 计算所有这些元素的 z-index 属性最大值 max, 将即将显示的遮罩 z-index 属性设置为 max+10, 将即将显示的 modal 的 z-index 属性设置为 max+20
let $beShowDialog = that.$element;
let $beShowBackDrop = that.$backdrop;
let zIndexAry = $.map($('.modal:visible,.modal-backdrop:visible'), function (obj) {
return $(obj).css('z-index');
});
let max = 1030;
if (zIndexAry && zIndexAry.length > 0) {
max = Math.max.apply(null, zIndexAry);
}
$beShowDialog.css('z-index', max + 20);
$beShowBackDrop.css('z-index', max + 10);
});
};
})(jQuery);
将以上代码拷贝到任意页面引入的 JS 文件里面即可。

