Patrick
Patrick

Reputation: 6948

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?

Upvotes: 90

Views: 217458

Answers (30)

Mr 1FORMATIK
Mr 1FORMATIK

Reputation: 1

You cannot use a setTimeout alone. You must use the await operator, the async declaration, and the promise object to pause, or sleep, in a JavaScript function.

The async and await functions let the code wait, indicating a promise to resolve or reject, before continuing to execute the code.

function pause(ms) 
{
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function example() 
{
  for (let i = 0; i < 100; i++) 
  {
    await pause(1000);
    document.write('Display for ' + i + ' seconds.<br>');
  }
}

example();

Upvotes: 0

laurago98
laurago98

Reputation: 1

The backdrop is removed correctly if I have these classes on the modal dialog: modal fade in. Originally I had modal fade, adding in solved my issue.

Upvotes: -1

Danger
Danger

Reputation: 1

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.

Upvotes: 0

Caesura
Caesura

Reputation: 11

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~

Upvotes: 1

T_O_Massey
T_O_Massey

Reputation: 1

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.

Upvotes: 0

Casey L
Casey L

Reputation: 707

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>

Upvotes: 2

Omeri
Omeri

Reputation: 347

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

Upvotes: 1

Zakaria Hafid
Zakaria Hafid

Reputation: 41

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

Upvotes: 4

Gampesh
Gampesh

Reputation: 4374

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

Upvotes: 124

bruTeFoRcE
bruTeFoRcE

Reputation: 37

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.

Upvotes: 0

parvej hussain
parvej hussain

Reputation: 31

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

Upvotes: 3

Mark Anthony Libres
Mark Anthony Libres

Reputation: 1054

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

Upvotes: 0

Abdelrahman Eldesoky
Abdelrahman Eldesoky

Reputation: 11

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

Upvotes: 1

Varinder Singh Baidwan
Varinder Singh Baidwan

Reputation: 203

for Bootstrap 4, this should work for you

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

Upvotes: 3

Rehan Rajpoot
Rehan Rajpoot

Reputation: 186

After perform action just trigger the close button.

Example

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

Upvotes: 2

Wellington Silva
Wellington Silva

Reputation: 27

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

Upvotes: 0

onhax
onhax

Reputation: 339

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

Upvotes: 0

SingeRoi
SingeRoi

Reputation: 1

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

See How to remove bootstrap modal overlay?

Upvotes: 0

deepak pudi
deepak pudi

Reputation: 11

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

Upvotes: 0

amitsin6h
amitsin6h

Reputation: 193

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.

Upvotes: 9

Tod
Tod

Reputation: 2524

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.

Upvotes: 0

Raj
Raj

Reputation: 61

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

Upvotes: 6

Possam
Possam

Reputation: 376

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

Upvotes: 0

JibstaMan
JibstaMan

Reputation: 498

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.

Upvotes: 38

Aakash
Aakash

Reputation: 23737

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.

Upvotes: 10

Ivan Koshelev
Ivan Koshelev

Reputation: 4260

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.

Upvotes: 3

Oliver S.
Oliver S.

Reputation: 109

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.

Upvotes: 1

leona
leona

Reputation: 31

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

Upvotes: 3

user2443343
user2443343

Reputation: 540

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.

Upvotes: 43

DIG
DIG

Reputation: 5754

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

Upvotes: 34

Related Questions