81

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?

Patrick
  • 6,828
  • 3
  • 23
  • 31
  • I'm not sure if this http://stackoverflow.com/questions/11519660/twitter-bootstrap-modal-backdrop-doesnt-disappear is the same issue. But your answer worked for me and seems much easier than any of those answers – Gustav May 08 '15 at 09:05

30 Answers30

113

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();
Sire
  • 4,086
  • 4
  • 39
  • 74
Gampesh
  • 4,024
  • 2
  • 12
  • 9
  • 1
    Thank you so much... The answers given by others are are not complete according to me. It just hides and not let you do another operation on back screen. Your code solved my problem. Thanks a lot once again Gampesh. – Kaushal Bhatt Jan 03 '16 at 06:23
  • 1
    Just sharing what happens with me, I do not have this problem until yesterday. All my modals as generated by some auto PHP code and JS. The problem starts happens when I put a modal ID as "modal". Just change it and stop the problem again. – mFontolan Jun 05 '16 at 13:10
  • Adding that this solved the problem for me using a postback triggered by a button within the modal in asp.net. The javascript above fixes the overlay issue when executed after the postback has completed. – J. Minjire Aug 26 '16 at 11:42
  • 1
    Thanks , this is only things worked for me. actually the only line i was neede is $('.modal-backdrop').remove(); – Benny Margalit Mar 13 '17 at 15:53
  • @visualjoel you can add scrollable css property if you would want to have scroll feature. – Gampesh Aug 02 '18 at 10:32
  • It works but I honestly don't know why we have to patch like this. Is there any kind if accurate detail about why the backdrop isn't removed ? – Marc Roussel Oct 15 '19 at 19:37
  • 3
    Ono more thing is needed: `$('body').css('paddingRight', 0);` Otherwise 17px padding was added to body as a result of the manual removal of the modal. – Jan Matousek Jun 07 '20 at 15:52
  • I haven't read the question, but if it was about closing the modal _and_ removing the backdrop, this works too: `$('.modal').modal('hide');` (it hides/removes both) – Sergey Zolotarev Apr 11 '23 at 14:56
83

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.

Patrick
  • 6,828
  • 3
  • 23
  • 31
41

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.

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
user2443343
  • 521
  • 4
  • 5
  • 2
    Deleting the class "fade" caused the UI not to behave in the default way. The modal just appeared instead of slowly fading in from the top. However, adding data-dismiss="modal" did solve the problem for me. I think it is a better approach than the accepted answer as this utilises the Bootstrap pre-defined data attributes, rather than changing the default UI and behaviour.. – gentrobot Mar 09 '16 at 03:49
  • 5
    Tha problem is, if you need to validate the form, data-dismiss will close the modal – gal007 Jun 28 '20 at 18:26
  • Was struggling with this, I was passing actually custom modal Id in place of just text as modal. Thanks to the question and answer. – Jatinder Aug 31 '21 at 14:29
38

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.

JibstaMan
  • 498
  • 4
  • 9
  • This helped me. I'm showing a `modal` on `ajaxStart()` and hiding it on `ajaxComplete()` so there were multiple instances of the backdrop when there were multiple `ajax` calls at once. Removing the `fade` class did the trick. Thank you! – JJJ Nov 03 '16 at 11:31
  • 1
    Is there a way to make 'fade' synchronous instead of asynchronous? – user2012677 Jun 05 '19 at 16:51
32

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">
DIG
  • 5,394
  • 2
  • 22
  • 17
10

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.

Aakash
  • 21,375
  • 7
  • 100
  • 81
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.

anothernode
  • 5,100
  • 13
  • 43
  • 62
amitsin6h
  • 173
  • 2
  • 8
  • 1
    Thanks. This seems working for my case. However, with bs 5, the 'data-bs-dismiss="modal"' must be used. – Dat TT Nov 22 '22 at 16:28
5

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

Raj
  • 51
  • 1
  • 1
3

just remove class 'fade' from modal

reznic
  • 672
  • 6
  • 9
3

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();
});
spenibus
  • 4,339
  • 11
  • 26
  • 35
leona
  • 31
  • 1
3

for Bootstrap 4, this should work for you

 $('.modal').remove();
 $('.modal-backdrop').remove();
Martin Wickman
  • 19,662
  • 12
  • 82
  • 106
3

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
Zaki Mohammed
  • 969
  • 14
  • 24
3

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();
  • This is the perfect solution in my case. I didn't want to load jQuery just to fix this! And all the other answers didn't work 100%. Clicking the Close button via plain Javascript is an awesome, complete solution, thanks! – i01573 Oct 28 '21 at 19:19
2

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.

Ivan Koshelev
  • 3,830
  • 2
  • 30
  • 50
2

After perform action just trigger the close button.

Example

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

Rehan Rajpoot
  • 158
  • 2
  • 6
2

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>
Casey L
  • 617
  • 10
  • 16
1

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.

Oliver S.
  • 109
  • 6
1

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

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();
    }
}
Omeri
  • 187
  • 2
  • 16
  • 2
    Sending a click event to the close button is an incredibly hacky solution that's worse than the other answers in the thread. – Aplet123 May 18 '21 at 18:24
1

Same issue even with Bootstrap 5.

I found that the body still had a style attached after the modal was closed :

overflow: hidden;
padding-right: 15px;

So I set $('body').removeAttr ('style'); And it bounced back. So then this worked for me : modal is the var created by new bootstrap.Modal and id is the id of the modal :

// Don't add data-bs-dismiss="modal" to the Cancel button.
//
modal.hide();

// Hack to remove any lingering backdrop.https://stackoverflow.com/questions/22056147/bootstrap-modal-backdrop-remaining
// Asked 8 years, 3 months ago ...
//
$('body').removeClass('modal-open');
$('.modal-backdrop').remove();

// My own observations - these are left as body styles after the modal is closed, removing the
// vertical scroll and offsetting the page.
//
// overflow: hidden;
// padding-right: 15px;
//
// Just setting removeAttr ('style') didn't work as it bounced back again, so 
// wait for modal to fire a hidden event.
//
$(id).on('hidden.bs.modal', function (e) {
    $('body').removeAttr ('style');
});

Hope that helps. ~ c~

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Caesura
  • 11
  • 1
0

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

Possam
  • 363
  • 4
  • 18
0

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.

Tod
  • 2,070
  • 21
  • 27
0

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

0

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

See How to remove bootstrap modal overlay?

Tlaloc-ES
  • 4,825
  • 7
  • 38
  • 84
0

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";
onhax
  • 199
  • 12
0

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

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');
});
Mark Anthony Libres
  • 906
  • 1
  • 7
  • 14
  • 3
    Please add an explanation, highlighting parts of your answer that addresses OP's issue, and why/how. Code only answers are frowned upon on SO. Adding details adds to long term value, and helps future visitors learn, so they can apply this knowledge to their own coding issues. Also, it improves quality of the answer, and hence SO as well. Higher quality answers are upvoted more often, as more users find them of greater use as a resource to refer back to. – SherylHohman Jul 07 '20 at 14:19
0

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.

bruTeFoRcE
  • 37
  • 6
0

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.

0

For anyone using Bootstrap 5 with NextJS,

const myBackdrop = document.getElementsByClassName("modal-backdrop");
myBackdrop[0].style.width = 0;
myBackdrop[0].style.height = 0;

Call this inside the button's onClick event.

Danger
  • 1
  • 1