Bootstrap Modal leaves orphaned backdrop in DOM after programmatic removal
0 reputation · 14 Sept 2023, 03:51 UTC
0 reputation · 14 Sept 2023, 03:51 UTC
I'm working with Bootstrap 5's Modal component and trying to understand the correct lifecycle when a modal element is removed from the DOM by application code rather than through the documented hide() method or data-bs-dismiss attribute.
My understanding is that Bootstrap creates the .modal-backdrop element dynamically, appends it to the body, and removes it only after the transitionend event fires during a normal hide cycle. The .modal-open class on the body, which controls the scroll lock, appears to be tied to that same lifecycle. What is unclear to me is what cleanup guarantees exist when the modal element itself is detached or destroyed externally — for example, when a SPA router or a framework re-renders the container holding the modal markup.
My specific questions:
hide(), is the backdrop guaranteed to be cleaned up, or does it persist as an orphaned element?dispose() before DOM removal also remove the backdrop and the .modal-open scroll lock?I'm assuming Bootstrap 5.3.x; if behavior differs across minor versions, please note that.
31775 reputation · 14 Sept 2023, 06:29 UTC
If a Bootstrap 5 modal element is removed from the DOM without calling hide(), the .modal-backdrop element and the .modal-open class on the body will persist as orphaned elements. Bootstrap does not use MutationObservers to track the modal element; it relies entirely on its internal lifecycle events to trigger cleanup.
Calling the dispose() method on the Modal instance is the correct way to handle programmatic removal. dispose() performs the following actions immediately:
.modal-backdrop element from the body..modal-open class from the body.For the safest result, call dispose() before removing the modal element from the DOM. While calling it after detachment may still work in version 5.3.x, disposing first ensures the instance has a valid reference to its target element during the cleanup process.
Bootstrap does not serialize rapid show() and hide() calls. If these methods are triggered faster than the CSS transitions can complete, you may encounter stacked backdrops or inconsistent UI states. To prevent this, use one of the following strategies:
show() call after the hidden.bs.modal event has fired for the previous instance.bootstrap.Modal.getOrCreateInstance(element) instead of new bootstrap.Modal(). Creating multiple instances for the same element is a common cause of orphaned backdrops.To verify your cleanup logic in a development environment, you can use the following scoped commands in the browser console:
// 1. Identify the modal instance
const modalEl = document.getElementById('myModal');
const modalInstance = bootstrap.Modal.getInstance(modalEl);
// 2. Dispose and remove
modalInstance.dispose();
modalEl.remove();
// 3. Verify body is clean
console.log('Backdrop exists:', !!document.querySelector('.modal-backdrop'));
console.log('Body is locked:', document.body.classList.contains('modal-open'));
This behavior is based on Bootstrap 5.3.x. While the dispose() method is standard in the 5.x series, minor version updates may alter the timing of transition events. If you are using a framework wrapper (e.g., React-Bootstrap or BootstrapVue), these libraries may implement their own cleanup hooks that override this native behavior.
Diagnostic Detail: Are you using a framework-specific wrapper or the vanilla Bootstrap JavaScript API? Wrappers often manage the lifecycle automatically, and manual disposal may conflict with the wrapper's internal state.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.