Search code examples
htmlcsstwitter-bootstrapz-index

Why is my modal backdrop above the modal?


Below you find a situation where the specified z-index of the modal is higher than the z-index of the modal-backdrop. Still, there are many situations that the backdrop will be on top of the modal. So, the user cannot access the modal. You find an example below in picture and code.

enter image description here

What are the rules for getting a modal always on top of the backdrop? Especially, I am looking for the 'position' options for the 'container' class. I simplified my issue by just using a simple container. In reality that container may have different positions. I used jQuery just to simplify the context.

Below you find HTML with 4 examples when a modal backdrop appears to be 'above' the modal. An example is:

Switching the backdrop 'off' is NOT a solution.

I read this great post and this article, but find it hard to apply to the deserved cases. I experimented with the 'stack context' in combination with placing the modal-divs inside or outside the container-div.

$(document).ready(function() {
  $("#myBtn").click(function() {
    $("#myModal").modal({
      backdrop: true
    });
  });
});
.container {
  z-index: 85;
  /* Backdrop overrules: situation 1: container position: relative; && modal position: no position with modal inside container-div */
  /* Backdrop overrules: situation 2: container position: absolute; && modal position: no position with modal inside container-div */
  /* Less important ... */
  /* Backdrop overrules: situation 3: container position: relative; && modal-div position: static with modal outside container-div */
  /* Backdrop overrules: situation 4: container position: absolute; && modal-div position: static with modal outside container-div */
  position: absolute;
}

.modal {
  background-color: red;
  z-index: 100;
}

.modal-backdrop {
  background-color: green;
  z-index: 90;
}
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>

<body>
  <div class="container">
    <h2>Modal and backdrops</h2>
    <p>Simulate a backdrop that overrules a modal</p>
    <button type="button" class="btn btn-info btn-md" id="myBtn">Modal with Overlay</button>

    <div class="modal fade" id="myModal" role="dialog">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal">&times;</button>
            <h4 class="modal-title">Modal with Overlay</h4>
          </div>
          <div class="modal-body">
            <p>This modal has a overlay.</p>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

Updates: removing the z-index from the modal is indeed correct. It will however not solve the current issue.

Digging deeper, I have these 2 screenshots. Before launching the popup. And after launching the popup; the modal-backdrop is dynamically added near the end of the DOM. I guess outside the context stack of the modal-div.

This is a situation inherited in maintaining a complex web page. The modal-div is inside a library component. The library comp is positioned with 'relative'.

enter image description here

... and after launching the popup:

enter image description here


Solution

  • I suggest you to slightly change the backdrop function in the bootstrap v3.4.1 modal, and insert .modal-backdrop directly into .modal:

    $(document).ready(function() {
    
      // https://github.com/twbs/bootstrap/blob/f17f882df292b29323f1e1da515bd16f326cee4a/js/modal.js#L186
        
        $.fn.modal.Constructor.prototype.backdrop = function (callback) {
         var Modal = $.fn.modal.Constructor /* + */
       var that = this
         var animate = this.$element.hasClass('fade') ? 'fade' : ''
    
        if (this.isShown && this.options.backdrop) {
          var doAnimate = $.support.transition && animate
    
          this.$backdrop = $(document.createElement('div'))
            .addClass('modal-backdrop ' + animate)
            // .appendTo(this.$body)
              this.$element.append(this.$backdrop); /* + */
    
          this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
            // if (this.ignoreBackdropClick) {
              // this.ignoreBackdropClick = false
              // return
            // }
            // if (e.target !== e.currentTarget) return
              if (e.target !== this.$backdrop[0]) return /* + */
            this.options.backdrop == 'static'
              ? this.$element[0].focus()
              : this.hide()
          }, this))
    
          if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
    
          this.$backdrop.addClass('in')
    
          if (!callback) return
    
          doAnimate ?
            this.$backdrop
              .one('bsTransitionEnd', callback)
              .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
            callback()
    
        } else if (!this.isShown && this.$backdrop) {
          this.$backdrop.removeClass('in')
    
          var callbackRemove = function () {
            that.removeBackdrop()
            callback && callback()
          }
          $.support.transition && this.$element.hasClass('fade') ?
            this.$backdrop
              .one('bsTransitionEnd', callbackRemove)
              .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
            callbackRemove()
    
        } else if (callback) {
          callback()
        }
      }
        
      $("#myBtn").click(function() {
        $("#myModal").modal({
          backdrop: true
        });
      });
      
    });
    .container {
      z-index: 85;
      /* Backdrop overrules: situation 1: container position: relative; && modal position: no position with modal inside container-div */
      /* Backdrop overrules: situation 2: container position: absolute; && modal position: no position with modal inside container-div */
      /* Less important ... */
      /* Backdrop overrules: situation 3: container position: relative; && modal-div position: static with modal outside container-div */
      /* Backdrop overrules: situation 4: container position: absolute; && modal-div position: static with modal outside container-div */
      position: absolute;
    }
    
    .modal {
      background-color: red;
    }
    
    .modal .modal-backdrop {
      background-color: green;
    }
    
    .modal-dialog {
      z-index: 999999;
    }
    <head>
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
      <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
      <div class="container">
        <h2>Modal and backdrops</h2>
        <p>Simulate a backdrop that overrules a modal</p>
        <button type="button" class="btn btn-info btn-md" id="myBtn">Modal with Overlay</button>
    
        <div class="modal fade" id="myModal" role="dialog">
          <div class="modal-dialog">
            <div class="modal-content">
              <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal">&times;</button>
                <h4 class="modal-title">Modal with Overlay</h4>
              </div>
              <div class="modal-body">
                <p>This modal has a overlay.</p>
              </div>
              <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </body>

    Any other modals will work as well.