'Bootstrap Modal Backdrop Remaining

I am showing a Bootstrap modal window for loading when performing AJAX calls. I broadcast a "progress.init" event when the loading modal should show and a "progress.finish" when I want the modal to hide. There are certain conditions where the modal is hidden very quickly after being shown, causing the modal backdrop to remain on the screen. Subsequent calls to hiding the element do not remove the backdrop. I also can't simply remove all backdrops as other modal windows might be displaying over the loading window. I have put together a jsfiddle demonstrating the problem with simply calling the modal function (instead of triggering events).

var loadingModal = $("#loadingModal");

$("#btnShow").click(function () {
   loadingModal.modal("show");
    //hide after 3 seconds
    setTimeout(function () {
        loadingModal.modal("hide");
    }, 3000);
});

$("#btnProblem").click(function () {
   //simulate a loading screen finishing fast, followed by another loading screen
    loadingModal.modal("show");
    loadingModal.modal("hide");
    loadingModal.modal("show");

    //Again simulate 3 seconds
    setTimeout(function () {
        loadingModal.modal("hide");
    }, 3000);
});

And the HTML:

<div id="loadingModal" class="modal fade">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-body">
        <p>Loading...</p>
      </div>
    </div>
  </div>
</div>
<button id="btnShow">Show Loading</button>
<button id="btnProblem">Cause show/hide problem</button>

Ideally, I would like to resolve this without waiting a specified time before calling modal("hide"). In other words, I want to try to avoid something like:

elem.on("progress.finish", function () {
    window.setTimeout(loadingModal.modal("hide");
}, 750);

I have also tried subscribing to the hidden.bs.modal and shown.bs.modal events from bootstrap to try to show/hide the element if it needs to be, but may be going about this wrong... Any ideas to allow this modal to show/hide?



Solution 1:[1]

Just in case anybody else runs into a similar issue: I found taking the class "fade" off of the modal will prevent this backdrop from sticking to the screen even after the modal is hidden. It appears to be a bug in the bootstrap.js for modals.

Another (while keeping the fade effects) would be to replace the call to jQueryElement.modal with your own custom javascript that adds the "in" class, sets display: block, and add a backdrop when showing, then to perform the opposite operations when you want to hide the modal.

Simply removing fade was sufficient for my project.

Solution 2:[2]

If after modal hide, faded background is remained and does not let you click any where you can forcefully remove those by using below piece of code.

First hide (all) your modal div elements.

$('.modal').modal('hide');

Secondly remove 'modal-open' class from body and '.modal-backdrop' at the end of the page.

$('body').removeClass('modal-open');
$('.modal-backdrop').remove();

Solution 3:[3]

Just in case anybody else runs into a similar issue: I kept the fade, but just added data-dismiss="modal" to the save button. Works for me.

Solution 4:[4]

The problem is that bootstrap removes the backdrop asynchronously. So when you call hide and show quickly after each other, the backdrop isn't removed.

The solution (as you've mentioned) is to wait for the modal to have been hidden completely, using the 'hidden.bs.modal' event. Use jQuery one to only perform the callback once. I've forked your jsfiddle to show how this would work.

// wait for the backdrop to be removed nicely.
loadingModal.one('hidden.bs.modal', function()
{
    loadingModal.modal("show");

    //Again simulate 3 seconds
    setTimeout(function () {
        loadingModal.modal("hide");
    }, 3000);
});
// hide for the first time, after binding to the hidden event.
loadingModal.modal("hide");

Looking through the code in Bootstrap:

This is what makes hiding the modal asynchronous:

$.support.transition && this.$element.hasClass('fade') ?
    this.$element
        .one('bsTransitionEnd', $.proxy(this.hideModal, this))
        .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
    this.hideModal()

This checks whether transitions are supported and the fade class is included on the modal. When both are true, it waits for the fade effect to complete, before hiding the modal. This waiting happens again before removing the backdrop.

This is why removing the fade class will make hiding the modal synchronous (no more waiting for CSS fade effect to complete) and why the solution by reznic works.

This check determines whether to add or remove the backdrop. isShown = true is performed synchronously. When you call hide and show quickly after each other, isShown becomes true and the check adds a backdrop, instead of removing the previous one, creating the problem you're having.

Solution 5:[5]

Workaround is to hide the backdrop entirely if you don't need one like this: data-backdrop=""

<div class="modal fade preview-modal" data-backdrop="" id="preview-modal"  role="dialog" aria-labelledby="preview-modal" aria-hidden="true">

Solution 6:[6]

This is what worked for me -

When the hidden.bs.modal event is fired, jQuery's .remove() function can remove the element that has .modal-backdrop class. So each time the modal is closed, the modal-backdrop will be removed.

//setting callback function for 'hidden.bs.modal' event
$('#modal').on('hidden.bs.modal', function(){
  //remove the backdrop
  $('.modal-backdrop').remove();
})

Good Luck.

Solution 7:[7]

The best and simple approach is by using the data-dismiss attribute. Basically the data-dismiss attribute will dismiss the modal and you won't see the backdrop remaining.

How to use data-dismiss attribute

All you need to do is adding the following at the place where you want to close your modal:

data-dismiss="modal" 

For example, I have a button and when someone clicks the button it will close the modal.

<button class="btn btn-info float-right" onclick="foo()" data-dismiss="modal">Save changes</button>

You can also handle the JavaScript function on onClick and it will close the modal and also run the JavaScript function.

This is the best approach to do using the data-dismiss attribute.

Solution 8:[8]

Add this:
??? $(".modal-backdrop").hide();
to the ng-click function in controller to prevent the fade-in backdrop from staying.

Solution 9:[9]

just remove class 'fade' from modal

Solution 10:[10]

After inspecting the element, the div with the modal-backdrop class still remains after I hit the browser back button, so I simply remove it:

$(window).on('popstate', function() {
    $(".modal-backdrop").remove();
});

Solution 11:[11]

for Bootstrap 4, this should work for you

 $('.modal').remove();
 $('.modal-backdrop').remove();

Solution 12:[12]

Use a close button in modal, this method is working fine:

<input type="button" class="btn btn-default" data-dismiss="modal" value="Cancel" id="close_model">

After success use:

$('#model_form').trigger("reset"); // for cleaning a modal form
$('#close_model').click(); // for closing a modal with backdrop

Solution 13:[13]

Another possible case (mine) - you are adding modal html dynamically to DOM and it contains more the 1 root node. Gotcha here is that comment nodes also count, so, if you copied template from bootstrap site - you have them.

Solution 14:[14]

After perform action just trigger the close button.

Example

$('.close').click();

Solution 15:[15]

To anyone still struggling with this issue, i found a work around

first add an id to your close button ( you can make it invisibile if you don't need or have one)

<button id="buttonDismiss" type="button" class="close" data-dismiss="modal" aria-label="Close">

and in your Ajax call trigger a click using Javascript and not Jquery( looses the references to your Modal)

document.getElementById("buttonDismiss").click();

Solution 16:[16]

try this

$('.modal').on('hide.bs.modal', function () {
    if ($(this).is(':visible')) {
       console.log("show modal")
       $('.modal-backdrop').show();
    }else{
        console.log("hidden modal");
        $('.modal-backdrop').remove();
    }
})

Solution 17:[17]

In order to go around the backdrop lingering after boostrap.js modal.hide(), I gave the close button in the modal an id, and I send a click event programmatically.

<div class="modal-header">
    <h5 class="modal-title">{{title}}</h5>
    <button 
        type="button" 
        class="btn-close" 
        data-bs-dismiss="modal" 
        aria-label="Close"
        :id="`${id}-btn-close`" ></button> 
</div>


...

hideModal (modalElementId = 'modal') {
    // Workaround issue with modal.hide() leaving the overlay behind.
    var modalCloseButton = document.getElementById(`${modalElementId}-btn-close`);
    if (modalCloseButton !== null) {
        modalCloseButton.click();
    }
}

Solution 18:[18]

Be carful adding {data-dismiss="modal"} as mentioned in the second answer. When working with Angulars ng-commit using a controller-scope-defined function the data-dismiss will be executed first and controller-scope-defined function is never called. I spend an hour to figure this out.

Solution 19:[19]

An issue I was having (may help someone) was simply that I was using a partial to load the Modal.

<li data-toggle="modal" data-target="#priceInfoModal">
<label>Listing Price</label>
<i class="fa fa-money"></i>
@Html.Partial("EditPartials/PriceInfo_Edit")
</li>

I had placed the partial call inside the same list item So data-target="#priceInfoModal" and div id="priceInfoModal" were in the same container causing me to not be able to close my modal

Solution 20:[20]

So if you don't want to remove fade or tinker with the dom objects, all you have to do is make sure you wait for the show to finish:

$('#load-modal').on('shown.bs.modal', function () {
    console.log('Shown Modal Backdrop');
    $('#load-modal').addClass('shown');
});

function HideLoader() {
    var loadmodalhidetimer = setInterval(function () {
        if ($('#load-modal').is('.shown')) {                
            $('#load-modal').removeClass('shown').modal('hide');
            clearInterval(loadmodalhidetimer);
            console.log('HideLoader');
        }
        else { //this is just for show.
            console.log('Waiting to Hide');
        }
    }, 200);
}

IMO Bootstrap should already be doing this. Well perhaps a little more, if there is a chance that you could be calling hide without having done show you may want to add a little on show.bs.modal add the class 'showing' and make sure that the timer checks that showing is intended before getting into the loop. Make sure you clear 'showing' at the point it's shown.

Solution 21:[21]

I got the same problem, the solution is add the property data-dismiss="modal" to the button you are going to click.

Solution 22:[22]

Add data-backdrop="false" option as an attribute to the button which opens the modal.

See How to remove bootstrap modal overlay?

Solution 23:[23]

Using the code below continuously adds 7px padding on the body element every time you open and close a modal.

$('modalId').modal('hide');
$('body').removeClass('modal-open');
$('.modal-backdrop').remove();`

For those who still use Bootstrap 3, here is a hack'ish workaround.

$('#modalid').modal('hide');
$('.modal-backdrop').hide();
document.body.style.paddingRight = '0'
document.getElementsByTagName("body")[0].style.overflowY = "auto";

Solution 24:[24]

Just create an event calling bellow. In my case as using Angular, just put into NgOnInit.

let body = document.querySelector('.modal-open');
body.classList.remove('modal-open');
body.removeAttribute('style');
let divFromHell = document.querySelector('.modal-backdrop');
body.removeChild(divFromHell);

Solution 25:[25]

If the close dismiss button is working but the problem occurs when using dynamic code such as modal('hide') you can use this code. forcing the backdrop to totally remove.

for Bootstrap 3

$modal.on('hidden.bs.modal', function(){
  //remove the backdrop
  jQuery('[data-dismiss="modal"]').click();
});

yourbutton.click(function() {
  $modal.modal('hide');
});

Solution 26:[26]

This is for someone who cannot get it fixed after trying almost everything listed above. Please go and do some basic checking like the order of scripts, referencing them and stuff. Personally, I used a bundle from bootstrap that did not let it work for some reason. Switching to separate scripts made my life a lot easier. So yeah! Just-in-case.

Solution 27:[27]

Not using AJAX, but also posting here for anyone else who is using the live demo Modal for Bootstrap 5. After assigning an onClick event to the Save Changes button, you can click it and the modal will close, but the backdrop will remain.

Adding data-bs-dismiss="modal" to the Save Changes button results in the backdrop being correctly removed upon modal dismissal.

Original code from the Bootstrap website:

<div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>

Version that works as expected:

<div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary" data-bs-dismiss="modal" onClick={(e) => { doSomething(e) }}>Save changes</button>
      </div>

Solution 28:[28]

Expanding upon @user2443343's and @Rory McCrossan's answer, while addressing @gal007's concern about not being able to validate a form when adding data-dismiss="modal" to the button element: we were on the right track, let's bring it all together.

$('#myButton').on('click', function(e) {    // using variable name "e" for the event
  var isValid = true;   // starting value is true, but set to false if any field is invalidated
  
  // (perform validation in script to ultimately get boolean value of "isValid"...)
  
  if (isValid === false) {
  
    // visually labeling the invalidated field (just an example)...
    $('#txtField1').removeClass('is-invalid').addClass('is-invalid');   
    // (whatever other css/visual warnings you want to give, and then...)
    
    isValid = true;   // reset value back to default
    e.preventDefault; // <------- TADA! This is the ticket, right here
    return false;     // Then you prematurely end the function here
    
  } else {
    
    // (perform script for when form is validated)
    
  }
  
});
<form>
  <!--fields/inputs go here...-->
  <button type="button" id="myButton" class="btn btn-secondary" data-dismiss="modal">Submit Request</button>
</form>

To summarize:

  1. Yes, do include data-dismiss="modal" in your button's attributes.
  2. Pass the event parameter in your button's click handler.
  3. If form fails validation in your script, call event.preventDefault and then return false, which effectively stops data-dismiss from being handled and exits your function.

This works beautifully for me.