bootstrap icon indicating copy to clipboard operation
bootstrap copied to clipboard

Can't close a Modal using JS

Open caub opened this issue 4 years ago • 21 comments

  • OS: linux
  • browser: Chrome latest
  • test-case: https://jsfiddle.net/caub/x6t2ovqm/

BS5-modal-hide-issue

As you can see, the backdrop is still there, after the modal gets closed

I could remove the backdrop myself document.querySelector('.modal-backdrop')?.remove() but that feels quite hacky, this internal part should be part of bootstrap API

caub avatar Apr 23 '21 07:04 caub

Thanks for reporting this problem. You're indeed correct with your expectation that this should be handled by our code.

As a workaround until it's fixed you may want to use:

const closeWithJS = document.getElementById('closeWithJS');
const openWithJS = document.getElementById('openWithJS');
const modalEl = document.getElementById('exampleModal');

closeWithJS.onclick = () => {
  const modal = bootstrap.Modal.getInstance(modalEl);
  modal.hide();
  modalEl.addEventListener('hidden.bs.modal', () => {
    modal.dispose();
  }, {once:true});
};

openWithJS.onclick = () => {
  const modal = new bootstrap.Modal(modalEl);
  modal.show();
};

cc @GeoSot

alpadev avatar Apr 23 '21 11:04 alpadev

It applies to all components. :fearful: But yes, we need to see this

GeoSot avatar Apr 23 '21 11:04 GeoSot

So do you guys want the hide method to work synchronously? :worried: Since the code that you wrote is fine IMO @alpadev

const modal = bootstrap.Modal.getInstance(modalEl);
modal.hide();
modalEl.addEventListener('hidden.bs.modal', () => {
  modal.dispose();
}, { once:true });

/CC @GeoSot

rohit2sharma95 avatar Apr 23 '21 19:04 rohit2sharma95

So do you guys want the hide method to work synchronously? 😟 Since the code that you wrote is fine IMO @alpadev

const modal = bootstrap.Modal.getInstance(modalEl);
modal.hide();
modalEl.addEventListener('hidden.bs.modal', () => {
  modal.dispose();
}, { once:true });

/CC @GeoSot

@rohit2sharma95 Hm not sure. I was thinking that it might be a bit tough to know how to do it the right way without knowing the code better. Maybe it would be enough to put an example into the docs.

alpadev avatar Apr 23 '21 20:04 alpadev

So do you guys want the hide method to work synchronously? 😟

No, for any reason. :) will brake many UX things for sure

After #33609 merge, we can discuss a holistic solution, but yet I am not sure, cause it would transform the dispose method, from sync to potential async

edit: modal, already uses a property to check its state this._isTransitioning

GeoSot avatar Apr 24 '21 15:04 GeoSot

Should there be something like a close function that will wrap all the:

modal.hide();
modalEl.addEventListener('hidden.bs.modal', () => {
  modal.dispose();
}, { once:true });

caub avatar Apr 24 '21 17:04 caub

Should there be something like a close function that will wrap all the:

I am not sure if this is a proper solution :thinking:

GeoSot avatar Apr 26 '21 09:04 GeoSot

Updated with the suggested solution: https://jsfiddle.net/caub/x6t2ovqm/5/ "Close with JS" works, we can close maybe

caub avatar May 25 '21 17:05 caub

@caub glad if that works for you.

I'd prefer to leave this open tho, so that we can track this and maybe at some point come up with an easier solution. Or maybe we should open a new feature issue for this. 🤔

/CC @GeoSot @rohit2sharma95

alpadev avatar May 25 '21 20:05 alpadev

I see when creating modal there are 2 div.modal-backdrop.show created on DOM. but when hiding the modal with JS it only removes 1 div.modal-backdrop.show so it still has the backdrop behind. Maybe this is the problem

dong94vk avatar Jun 05 '21 20:06 dong94vk

Thanks for reporting this problem. You're indeed correct with your expectation that this should be handled by our code.

As a workaround until it's fixed you may want to use:

const closeWithJS = document.getElementById('closeWithJS');
const openWithJS = document.getElementById('openWithJS');
const modalEl = document.getElementById('exampleModal');

closeWithJS.onclick = () => {
  const modal = bootstrap.Modal.getInstance(modalEl);
  modal.hide();
  modalEl.addEventListener('hidden.bs.modal', () => {
    modal.dispose();
  }, {once:true});
};

openWithJS.onclick = () => {
  const modal = new bootstrap.Modal(modalEl);
  modal.show();
};

cc @GeoSot

I'm using it in reactjs but it's not work :(

dong94vk avatar Jun 05 '21 20:06 dong94vk

Ran into this issue when looking into using bootstrap 5 with Angular. Long story short:

Do NOT mix bootstrap's data attributes with javascript calls. 

If you need a modal to display content with no other user interaction except dismissing the modal then the data attribute approach is simple and works fine. However, if you have user interaction in your modal then remove all data-bs-**** attributes and call the hide() on the Modal type.

The following Angular code works as expected.

app.component.ts

import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
import { Modal } from 'bootstrap';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit {

  @ViewChild('exampleModal') exampleModalRef!: ElementRef;

  exampleModal!: Modal;

  ngAfterViewInit(): void {
    this.exampleModal = new Modal(this.exampleModalRef.nativeElement);
  }

  showModal(): void {
    this.exampleModal.show();
  }

  hideModal(): void {
    this.exampleModal.hide();
  }

  saveChanges(e: any): void {
    console.log('save changes clicked');
    this.hideModal();
  }
}

app.component.html

<div class="container">
  <!-- Button trigger modal -->
  <button type="button" class="btn btn-primary" (click)="showModal()">
    Launch demo modal
  </button>

  <!-- Modal -->
  <div class="modal fade" #exampleModal id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
          <button type="button" class="btn-close" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          ...
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" (click)="hideModal()">Close</button>
          <button type="button" class="btn btn-primary" (click)="saveChanges($event)">Save changes</button>
        </div>
      </div>
    </div>
  </div>
</div>

miguellira avatar Feb 24 '22 05:02 miguellira

I have the same problem and I am not using any JS framework, just vanilla javascript.

I have a form inside a modal and I need to asynchronously close the modal on submit. In BS4 $('#modal').modal('hide') works as expected. In BS5 I end up with this hack:

  const bsModal = Modal.getInstance($modal)
  bsModal._isShown = true
  bsModal.hide()
  document.querySelector('.modal-backdrop').classList.remove('show')

bluesurfer avatar Oct 27 '22 15:10 bluesurfer

@bluesurfer Does your template contain any data-bs-xxxx attributes?

miguellira avatar Oct 27 '22 15:10 miguellira

@miguellira mmm actually no.. I am using javascript to populate the content of the modal (see https://getbootstrap.com/docs/5.2/components/modal/#varying-modal-content)

bluesurfer avatar Oct 27 '22 16:10 bluesurfer

What about the modal itself? Maybe top right close button? Check the emitted DOM. TBH it's been months since I posted in this thread and as I mentioned it all worked fine once I removed all data-bs attributes.

miguellira avatar Oct 27 '22 16:10 miguellira

@miguellira wait are you telling me not to use any data-bs attribute not even for popover or tooltip? I am simply not using any data-bs-modal but there lots of otherdata-bs-xxxx attributes

bluesurfer avatar Oct 28 '22 08:10 bluesurfer

@miguellira aah I solve it.. For me the problem are not the data attributes but the fact that when I was creating/updating the modal through Javascript I kept calling new Modal. That makes my code "ill" at the very beginning 😄

Using Modal.getOrCreateInstance at modal creation/update and then Modal.getInstance to close it at form submit (in my case) did the trick: the hide() method works as expected.

bluesurfer avatar Oct 28 '22 08:10 bluesurfer

modal object should be initialized out of the click event, in this way it worked for me.

        const modalEle: HTMLElement = document.querySelector('#js-modal');
        const modal = new Modal(modalEle, {});

        document.querySelector('.close-button').addEventListener('click', (evt: Event) => {
            if (modalEle){
                modal.hide();
            }
        });

dinoop-caseking avatar Mar 23 '23 14:03 dinoop-caseking

Ran into this issue when looking into using bootstrap 5 with Angular. Long story short:

Do NOT mix bootstrap's data attributes with javascript calls. 

Thanks! Seems like showing and hiding a modal using Bootstrap 5 and Vue 3 should be handled explicitly through the JS methods and entirely without the data-bs- attributes. I don't know why though.

Maritims avatar Aug 15 '23 21:08 Maritims

Ran into this issue when looking into using bootstrap 5 with Angular. Long story short:

Do NOT mix bootstrap's data attributes with javascript calls. 

Thanks! Seems like showing and hiding a modal using Bootstrap 5 and Vue 3 should be handled explicitly through the JS methods and entirely without the data-bs- attributes. I don't know why though.

I can confirm this fixed the issue for me in Bootstrap 5.3.8. I removed all data- attributes and handled the modal entirely with JavaScript for initialization, showing, and hiding. Commenting this here in case someone is still looking for a solution in 2025+.

sven-straubinger avatar Oct 01 '25 08:10 sven-straubinger