Bootstrap Modal Backdrop Remaining

JavascriptTwitter Bootstrap-3

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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 2 - Javascript

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 3 - Javascript

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

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

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

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

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

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

Solution 9 - Javascript

just remove class 'fade' from modal

Solution 10 - Javascript

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

for Bootstrap 4, this should work for you

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

Solution 12 - Javascript

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

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

After perform action just trigger the close button.

Example

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

Solution 15 - Javascript

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

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

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

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

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

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

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

Solution 22 - Javascript

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

See https://stackoverflow.com/questions/32459337/how-to-remove-bootstrap-modal-overlay

Solution 23 - Javascript

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

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

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

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

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

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPatrickView Question on Stackoverflow
Solution 1 - JavascriptGampeshView Answer on Stackoverflow
Solution 2 - JavascriptPatrickView Answer on Stackoverflow
Solution 3 - Javascriptuser2443343View Answer on Stackoverflow
Solution 4 - JavascriptJibstaManView Answer on Stackoverflow
Solution 5 - JavascriptDIGView Answer on Stackoverflow
Solution 6 - JavascriptAakashView Answer on Stackoverflow
Solution 7 - Javascriptamitsin6hView Answer on Stackoverflow
Solution 8 - JavascriptRajView Answer on Stackoverflow
Solution 9 - JavascriptreznicView Answer on Stackoverflow
Solution 10 - JavascriptleonaView Answer on Stackoverflow
Solution 11 - JavascriptVarinder Singh BaidwanView Answer on Stackoverflow
Solution 12 - Javascriptparvej hussainView Answer on Stackoverflow
Solution 13 - JavascriptIvan KoshelevView Answer on Stackoverflow
Solution 14 - JavascriptRehan RajpootView Answer on Stackoverflow
Solution 15 - JavascriptZakaria HafidView Answer on Stackoverflow
Solution 16 - JavascriptAbdelrahman EldesokyView Answer on Stackoverflow
Solution 17 - JavascriptOmeriView Answer on Stackoverflow
Solution 18 - JavascriptOliver S.View Answer on Stackoverflow
Solution 19 - Javascriptsam80eView Answer on Stackoverflow
Solution 20 - JavascriptTodView Answer on Stackoverflow
Solution 21 - Javascriptdeepak pudiView Answer on Stackoverflow
Solution 22 - JavascriptSingeRoiView Answer on Stackoverflow
Solution 23 - JavascriptonhaxView Answer on Stackoverflow
Solution 24 - JavascriptWellington SilvaView Answer on Stackoverflow
Solution 25 - JavascriptMark Anthony LibresView Answer on Stackoverflow
Solution 26 - JavascriptbruTeFoRcEView Answer on Stackoverflow
Solution 27 - JavascriptCasey LView Answer on Stackoverflow
Solution 28 - JavascriptT_O_MasseyView Answer on Stackoverflow