Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Bootstrap Modal Dialog showing under Modal Background


On this page:

I've repeatedly run into the following problem with Bootstrap's modal dialog where the dialog ends up showing underneath the modal background:

Ugh. This problem seems to have arrived sometime around Bootstrap 3.33 - prior to that things seemed to work just fine but starting with 3.34 and later things started going south.

Caused by positioning

The problem is that many modern applications use fixed, absolute and relative positioning to lay out their user interface. The above problem occurs when the Modal dialog sits within a container that has any parent that uses either fixed, absolute or relative positioning. The change post 3.34 of bootstrap was that the modal overlay was moved to the DOM root below <body> while the actual modal dialog content in this case is sitting inside of a separate nested DOM structure.

<body>

<app>
    <div style="position: absolute">
        ... other content
        <div class="modal">
            ... modal dialog here
        </div>
    <div>
<app>

<div class="modal-overlay"></div>
</body>

The problem occurs because the overlay and the other content container are not relative to each other so the position:absolute (or fixed) and their z-index values are not directly related to each other.

The easiest solution is to move the modal dialog outside of any container and just declare it under the <body> element, or - remove any absolute, fixed or relative positioning.

<body>

<app>
    <div style="position: absolute">
        ... other content
    <div>
<app>


<div class="modal">
    ... modal dialog here
</div>
<div class="modal-overlay"></div>
</body>

This setup works because there's no top level container above the modal that introduces it's own positioning root.

Problem solved, right?

Not so fast

Well not quite. If you can move your elements - great, but in dynamic applications rendered through a framework like Angular, it's likely you have a component rendering the modal, and you can't control where the element goes. In the first example the <app> tag is the Angular application's DOM root and everything rendered dynamically by Angular renders below it. If anything in that tree uses position: absolute or position:fixed - boom the problem shows up in modals.

The problem is that the .modal-backdrop overlay sits in the root of the DOM tree while the other content is buried in a separate DOM node tree.

If anything inside of that tree is relatively positioned, the z-index can no longer be compared effectively. I tried with a number of values but it just doesn't work in this scenario:

.modal-backdrop {
    z-index: 1040 !important;
}
.modal-dialog {
    margin: 2px auto;
    z-index: 1100 !important;
}

One workaround is to essentially force the modal out to the body tag with script. Since I trigger the modal in code anyway, it's easy enough to do the following:

showEditor() {
    $("#EditModal").modal("show");
    $("#EditModal").appendTo("body");
}

This works - sort of. But... this has a nasty side effect as well: Because Angular re-renders the view every time it's accessed, a new #EditModal element is created each and everytime. After a few accesses the 'moved' elements start piling up on the bottom of the page:

Not only that - this behavior also causes duplication of the the elements and on occasion causes the wrong data to come up.

In my case, because I'm using Angular 2.0 and I control the code, I can fix this by clearing out the modal when I exit the view:

showEditor() {
  $("#EditModal").modal("show");
  $("#EditModal").appendTo("body");
}

ngOnDestroy(){
  $("body>#EditModal").remove();
}

And this actually works. I get the modal that pops up and gets moved, and I can access it. When I leave the view the extraneous, moved DOM reference is cleaned up.

This works in a pinch but it's clearly not a generic solution.

Remove the Backdrop

There are a host of other solutions that work around this same basic theme moving the modal out of the DOM tree to the root, but I found all of them lacking or not working in some situation.

The only reliable way to get modals to work that I found to fix this entire mess is to simply disable the backdrop altogether. Add this CSS to your global stylesheet:

.modal-backdrop {
    /* bug fix - no overlay */    
    display: none;    
}

And the modal dialog will work, albeit without the black overlay:

While this isn't ideal, it does solve the problem cleanly in one place and for all scenarios, so this is clearly the no hassle solution. The black background would be nice but not if it's a detriment to making the application behave incorrectly.

You can also turn off the background on a case by case basis using an attribute on the modal dialog:

<div class="modal fade" id="createModal" data-backdrop="false">
   ...
</div>

which works if you have a mixed bag of dialogs that are perhaps defined globally and statically at the root, along with application generated and embedded modals.

Summary

This isn't exactly news, but after repeatedly running into this problem I decided to write it down so I can remember how the heck to fix it next time it happens.

To summarize:

  • If possible move the Modal into the <body> element
  • If possible have no position fixed, absolute or relative elements above the .modal
  • If possible use code tomove the .modal to the <body> tag, and remove or move back when done
  • If all else fails, remove the background on .modal-background with display: none or the data-background=false attribute.

It's crazy to think that the Bootstrap folks have decided to not fix this common issue. The volume of answers and comments on this StackOverflow issue as well as the GitHub issue are huge, yet this hasn't been addressed in well over a year and a half. There's a supposed fix (which moves the overlay into the same DOM tree as the modal) but experimenting with that by explicitly moving it in code just proved to be even weirder with strange opacity behavior and other weird artificats.

For me personally the display:none trick is what I went with because it just works without any changes.

If anybody can think of a way we could make this work in a dynamic application framework like Angular, leave a comment...

Resources

Posted in CSS  Angular  

The Voices of Reason


 

Matt Cannon
November 16, 2017

# re: Bootstrap Modal Dialog showing under Modal Background

How about just moving the modal on ngOnInit?

ngOnInit() { //Move the modal by Appending it to the body otherwise it appears behind the shade $('#EditModal').appendTo('body') }


Matt Cannon
November 16, 2017

# re: Bootstrap Modal Dialog showing under Modal Background

How about just moving the modal on ngOnInit? This resolved the issue for me... I first moved it to body then realized your issue with having to remove the elements on destroy... So I just moved them to the outermost element instead, so no need to destroy...

ngOnInit() { //Move the modal by Appending it to the body otherwise it appears behind the shade $('#EditModal').appendTo('.page-content') }


Rick Strahl
November 16, 2017

# re: Bootstrap Modal Dialog showing under Modal Background

@Matt - that may work in some instances but not always. I've experimented with a lot of different things and the only things that work reliably are not using a background and delay updating the z-order (out of band).


Adam
January 10, 2018

# re: Bootstrap Modal Dialog showing under Modal Background

I ran into the same problem using bootstrp-dialog and this worked for me. Has the added bonus that the dialog doesn't close if you accidently click on the background.


Dejan
February 14, 2018

# re: Bootstrap Modal Dialog showing under Modal Background

I solved it like you said and to simulate that backdrop I simply add background to modal in rgba format

.modal-backdrop {
  display: none;
}

.modal {
  background: rgba(0,0,0,0.5);
}

Glade Mellor
March 29, 2018

# re: Bootstrap Modal Dialog showing under Modal Background

I just commented out the position: fixed style in the _modal.css file for .modal-backdrop and that seems to work for me.

// Modal background .modal-backdrop { /* commented out position: fixed - bug fix */ //position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: $zindex-modal-backdrop; background-color: $modal-backdrop-bg; // Fade for backdrop &.fade { opacity: 0; }

Thanks for the guidance!


SamRay1024
April 12, 2018

# re: Bootstrap Modal Dialog showing under Modal Background

I ran into the same problem and I found another solution that was fine for my use case : move the .modal-backdrop div which is inserted to the DOM under on modal opening next to the modal.

var $Modal = $('#my-modal);
			
$Modal.modal('show');

if ($Modal.parent().get(0).tagName != 'BODY')
	$('.modal-backdrop').insertAfter($Modal);

It works fine since when the modal is closed, the .modal-backdrop is removed.


Adam J. Mendoza
May 25, 2018

# re: Bootstrap Modal Dialog showing under Modal Background

Thanks. I ran into this bug using Angular 6 and ng2-bs3-modal package.

 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2018