Search code examples
htmlcssscrolloverflow

HTML `dialog` element: scroll content independently of background


I am trying to use the dialog element.

When the dialog/modal is closed, the body should be scrollable.

When the dialog/modal is open, if it has large contents, the dialog/modal should be scrollable.

However, when the dialog/modal is open, I don't want scroll to apply to both the dialog/modal and the body background, which is what it seems to do by default.

Example: https://output.jsbin.com/mutudop/3.

How can I make scroll apply only to the dialog/modal contents, when the dialog/modal is open?

Note: I am only interested in solutions using the native dialog element.


Solution

  • So I tried it as well and came up with this:

    (function() {
      var openBtn = document.querySelector("button#open");
      var myDialog = document.querySelector("dialog");
    
      openBtn.addEventListener('click', function() {
        if (typeof myDialog.showModal === "function") {
          myDialog.showModal();
          document.querySelector("body").classList.add("overflow-hidden");
        } else {
          alert("Dialog API not supported by browser");
        }
      });
    })();
    * {
      box-sizing: border-box;
    }
    
    .wrapper {
      height: 10000px;
    }
    
    dialog {
      width: 100%;
      height: 100%;
      padding: 0;
      margin: 0;
      border: 0;
      z-index: 100;
      background: transparent;
      overflow-y: auto;
    }
    
    dialog>div {
      width: 50%;
      height: 500px;
      background: white;
      border: 3px solid black;
      margin: 0 auto;
      margin-top: 50px;
    }
    
    .overflow-hidden {
      overflow: hidden;
    }
    <div class="wrapper">
      <dialog>
        <div>
          <form method="dialog">
            <button onclick='document.body.classList.remove("overflow-hidden");' value="cancel">Cancel</button>
          </form>
        </div>
      </dialog>
    
      <button id="open">Open Dialog</button>
      <h4>You can scroll the body now but not when the dialog is opened.</h4>
    </div>

    You might have noticed that I added two lines of JS to hide/show the overflow of the body and you will probably need them as you can't target the body with pure CSS if you want to check if the dialog is opened or not.

    If you don't want them you can remove them and it just works fine. However, you will have two scroll bars on the right side. This is how it looks without the JS:

    (function() {
      var openBtn = document.querySelector("button#open");
      var myDialog = document.querySelector("dialog");
    
      openBtn.addEventListener('click', function() {
        if (typeof myDialog.showModal === "function") {
          myDialog.showModal();
        } else {
          alert("Dialog API not supported by browser");
        }
      });
    })();
    * {
      box-sizing: border-box;
    }
    
    .wrapper {
      height: 10000px;
    }
    
    dialog {
      width: 100%;
      height: 100%;
      padding: 0;
      margin: 0;
      border: 0;
      z-index: 100;
      background: transparent;
      overflow-y: auto;
    }
    
    dialog>div {
      width: 50%;
      height: 500px;
      background: white;
      border: 3px solid black;
      margin: 0 auto;
      margin-top: 50px;
    }
    
    .overflow-hidden {
      overflow: hidden;
    }
    <div class="wrapper">
      <dialog>
        <div>
          <form method="dialog">
            <button value="cancel">Cancel</button>
          </form>
        </div>
      </dialog>
    
      <button id="open">Open Dialog</button>
    </div>

    If you need any explanation let me know but I believe the code should be self-explanatory.