[javascript] 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?

This question is related to javascript twitter-bootstrap-3

The answer is


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);

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


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

See How to remove bootstrap modal overlay?


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">

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

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.


After perform action just trigger the close button.

Example

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


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.


for Bootstrap 4, this should work for you

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

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');
});

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.


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();

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();
});

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.


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();
    }
})

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


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


just remove class 'fade' from modal


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.


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.


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";

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.


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.