HTML `dialog` 元素:独立于背景滚动内容

HTML `dialog` element: scroll content independently of background

我正在尝试使用 dialog 元素。

当 dialog/modal 关闭时,正文应该是可滚动的。

当dialog/modal打开时,如果内容较大,dialog/modal应该是可滚动的。

但是,当 dialog/modal 打开时,我不想滚动应用到 dialog/modal 正文背景,这是默认情况下它似乎在做什么。

示例:https://output.jsbin.com/mutudop/3.

当 dialog/modal 打开时,如何使滚动仅应用于 dialog/modal 内容?

注意:我只对使用原生 dialog 元素的解决方案感兴趣。

更新

我创建了另一个示例,其中如果您的主要内容大于您的主要内容,则您的主要内容不会随您的模式一起滚动。您可以在您的容器上将 position 设置为 fixed 以实现此目的。

(function() {
  var openBtn = document.getElementById('open-dialog');
  var myDialog = document.getElementById('my-dialog');

  openBtn.addEventListener('click', function() {
    if (typeof myDialog.showModal === "function") {
      myDialog.showModal();
    } else {
      alert("Dialog API not supported by browser");
    }
  });

})();
#container {
  height: 100vh;
  width: 100vw;
  position: fixed;
  top: 0;
  left: 0;
  background: #ccc;
}

#my-dialog {
  margin-top: 1rem;
  margin-bottom: 3rem;
  top: 3rem;
  width: 50%;
  overflow-y: auto;
}

#my-dialog__content {
  display: flex;
  flex-direction: column;
  height: 200vh;
}

menu {
  width: 100%;
  padding: 0;
  margin: 0 auto;
}

#cancel-button {
  width: 100%
}
<div id="container">
  <dialog id="my-dialog">
    <div id="my-dialog__content">
      <form method="dialog">
        <menu>
          <button id="cancel-button" value="cancel">Cancel</button>
        </menu>
      </form>
    </div>
  </dialog>

  <menu>
    <button id="open-dialog">Open Dialog</button>
  </menu>
</div>

原回答

您可以在对话框中设置 max-height 并相应地设置对话框内容的样式。请参阅下面的示例。

(function() {
  var openBtn = document.getElementById('open-dialog');
  var myDialog = document.getElementById('my-dialog');

  openBtn.addEventListener('click', function() {
    if (typeof myDialog.showModal === "function") {
      myDialog.showModal();
    } else {
      alert("Dialog API not supported by browser");
    }
  });

})();
#my-dialog {
  width: 50%;
  max-height: 50vh;
  overflow-y: auto;
}

#my-dialog__content {
  display: flex;
  flex-direction: column;
  height: 150vh;
}

menu {
  width: 100%;
  padding: 0;
  margin: 0 auto;
}

#cancel-button {
  width: 100%
}
<div id="container">
  <dialog id="my-dialog">
    <div id="my-dialog__content">
      <form method="dialog">
        <menu>
          <button id="cancel-button" value="cancel">Cancel</button>
        </menu>
      </form>
    </div>
  </dialog>

  <menu>
    <button id="open-dialog">Open Dialog</button>
  </menu>
</div>

简单的解决方案是:显示 mnodel 后,再制作一个 DIV 作为覆盖全屏的覆盖层,在那个地方 css { pointer-events:none } 和模型将被放置在上面。用户不能点击模型数据以外的正文内容。

我已经创建了样本:http://jsfiddle.net/z3sgvnox/

<body id="content-body">


<div id="container">
  <dialog id="my-dialog">
    <div id="my-dialog__content">
      <form method="dialog">
        <menu>
          <button id="cancel-button" value="cancel">Cancel</button>
        </menu>
      </form>
    </div>
  </dialog>

  <menu>
    <button id="open-dialog">Open Dialog</button>
  </menu>
</div>
</body>

CSS

#container {
  height: 100vh;
  width: 100vw;
  position: fixed;
  top: 0;
  left: 0;
  background: #ccc;
}

#my-dialog {
  margin-top: 1rem;
  margin-bottom: 3rem;
  width: 50%;
  overflow-y: auto;
      max-height: 80%;
}
.hideScroll{
  overflow:hidden;
  pointer-events:none;
}

#my-dialog__content {
  display: flex;
  flex-direction: column;
  height: 200vh;
}

menu {
  width: 100%;
  padding: 0;
  margin: 0 auto;
}

#cancel-button {
  width: 100%
}

JS:

(function() {
  var openBtn = document.getElementById('open-dialog');
  var myDialog = document.getElementById('my-dialog');
var bodyData = document.getElementById('content-body');
  openBtn.addEventListener('click', function() {
    if (typeof myDialog.showModal === "function") {
      myDialog.showModal();
      bodyData.classList.add("hideScroll");
    } else {
      alert("Dialog API not supported by browser");
    }
  });

})();

所以我也试了一下,想出了这个:

(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>

您可能已经注意到我在 hide/show 和 bodyoverflow 中添加了两行 JS,您可能需要它们,因为您无法定位 body 与纯 CSS 如果你想检查 dialog 是否打开。

如果您想要它们,您可以删除它们并且效果很好。但是,您将在右侧有两个滚动条。这是没有 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>

如果您需要任何解释,请告诉我,但我认为代码应该是 self-explanatory。

这个答案考虑了转义键。我将 keydown 事件侦听器添加到 document.documentElement 而不是实际的 dialog 元素。这是因为当 dialog 有一个 keydown 事件侦听器时,它并不总是触发。例如,如果 dialog 打开并且其中的按钮具有焦点并且您按下转义键,keydown 事件侦听器 将触发 。但是让我们假设 dialog 中有一些文本,您突出显示该文本,然后按转义键。在这种情况下,keydown 事件侦听器将不会 触发。

const activeModals = [];

function openModal(dialogSelector) {
  const dialog = document.querySelector(dialogSelector);
  dialog.showModal();
  activeModals.push(dialog);
  document.body.classList.add('overflow-hidden');
}

function closeActiveModal() {
  const activeModal = activeModals.pop();
  activeModal.close();

  if (activeModals.length === 0) {
    document.body.classList.remove('overflow-hidden');
  }
}

document.documentElement.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && activeModals.length) {
    e.preventDefault();
    closeActiveModal();
  }
});

document.querySelectorAll('[data-toggle="modal"]').forEach((button) => {
  button.addEventListener('click', () => {
    openModal(button.getAttribute('data-target'));
  });
});

document.querySelectorAll('[data-dismiss="modal"]').forEach((button) => {
  button.addEventListener('click', closeActiveModal);
});

let fillerHtml = '';

for (let i = 1; i <= 100; i++) {
  fillerHtml += `<p>${i}</p>`;
}

document.querySelectorAll('.filler').forEach((div) => {
  div.innerHTML = fillerHtml;
});
.overflow-hidden {
  overflow: hidden;
}

p {
  font-size: 20px;
}
<button data-toggle="modal" data-target="#dialog1">Open Dialog 1</button>

<dialog id="dialog1">
  <h1>Dialog 1</h1>
  <button data-dismiss="modal">Close Dialog 1</button>
  <button data-toggle="modal" data-target="#dialog2">Open Dialog 2</button>
  <div class="filler"></div>
</dialog>

<dialog id="dialog2">
  <h1>Dialog 2</h1>
  <button data-dismiss="modal">Close Dialog 2</button>
</dialog>

<div class="filler"></div>