DEV Community

Andrew Elans
Andrew Elans

Posted on • Edited on

Fix Bootstrap 5.3 modal's rootElement

When placing a Bootstrap 5.3 modal inside a container other than body, bootstrap's <div class="modal-backdrop fade show"></div>, that is a dark half-transparent layer under modal, is always appended to body instead of the specified container, giving wrong behavior as explained here or here.

Backdrop's source code has this part:

const Default = {
  className: 'modal-backdrop',
  clickCallback: null,
  isAnimated: false,
  isVisible: true, // if false, we use the backdrop helper without adding any element to the dom
  rootElement: 'body' // give the choice to place backdrop under different elements
}
Enter fullscreen mode Exit fullscreen mode

However there is no mechanism provided to specify rootElement in the bootstrap options.

I fixed as follows in bootstrap.bundle.js version v5.3.3

  1. Find class Backdrop extends Config, there _configAfterMerge(config)
  2. Replace config.rootElement = ... with config.rootElement = getElement(config.rootElement) || document.body; with will fallback to body if rootElement is not found, ie. null returned from getElement():
_configAfterMerge(config) {
      // use getElement() with the default "body" to get a fresh Element on each instantiation
      config.rootElement = getElement(config.rootElement) || document.body;
      return config;
}
Enter fullscreen mode Exit fullscreen mode
  1. Find class Modal extends BaseComponent, there _initializeBackDrop()
  2. add after isAnimated: this._isAnimated() a new property rootElement: this._config.rootElement:
_initializeBackDrop() {
      return new Backdrop({
        isVisible: Boolean(this._config.backdrop),
        // 'static' option will be translated to true, and booleans will keep their value,
        isAnimated: this._isAnimated(),
        rootElement: this._config.rootElement
      });
}
Enter fullscreen mode Exit fullscreen mode

When initializing your bootstrap with new bootstrap add rootElement: <your container: HTMLElement as object or string selector>, for example:

const myModal = new bootstrap.Modal(
    document.getElementById('myModal')
    , {
        backdrop: "static", 
        rootElement: '#your-div'
    }
)
myModal.show()
Enter fullscreen mode Exit fullscreen mode

Here's my usage in SPA.

// I have a dynamic modal in my SPA, so I'm rendering a modal
// inside a DocumentFragment first and save this modal object
// in a variable so that I can call modal's methods after, like close()

const VALUES = {
    modal: {
        id: 'my-modal',
        obj: null
    }
}

const fragment = document.createRange().createContextualFragment(
    `<div class="modal fade" id="my-modal" tabindex="-1">
     ...
     </div>`
)
const rootElement = document.getElementById('my-container');
VALUES.modal.obj = new bootstrap.Modal(
    fragment.getElementById('my-modal')    
    , {
        backdrop: "static", 
        rootElement
    }
)
rootElement.append(fragment)
VALUES.modal.obj.show()
Enter fullscreen mode Exit fullscreen mode

Top comments (0)