【问题标题】:How do I close dialog by clicking outside of it?如何通过在对话框外单击来关闭对话框?
【发布时间】:2023-02-16 19:00:50
【问题描述】:

除了单击 Esc 按钮之外,您如何通过单击外部来关闭 dialog 框?

<!DOCTYPE html>
<html>
<body>

<p>Click the button to show the dialog.</p>

<button onclick="myFunction()">Show dialog</button>

<p><b>Note:</b> Use the "Esc" button to close the modal.</p>
<p><b>Note:</b> The dialog element is only supported in Chrome 37+, Safari 6+ and Opera 24+.</p>

<dialog id="myDialog">This is a dialog window</dialog>

<script>
function myFunction() { 
  document.getElementById("myDialog").showModal(); 
} 
</script>

</body>
</html>

【问题讨论】:

标签: javascript html


【解决方案1】:

HTMLDialogElement 接口的 showModal() 方法将对话框显示为模式,位于可能存在的任何其他对话框的顶部。它与 ::backdrop 伪元素一起显示到顶层。对话框外的交互被阻止,并且对话框外的内容呈现为惰性。 MDN documentation

如您所见,在对话所有其他 html 节点将被阻止。但是,我们可以添加一个子节点作为我们内容的包装器,这样我们就可以将我们的内容与外部区域分开。让我们从中删除额外的填充对话使用 css 并将其添加到模态类。

const dialog = document.getElementById('myDialog');
const button = document.querySelector('button');

function openDialog() {
  dialog.showModal();
}

function closeDialog(event) {
  // If the target dialog is
  if (!event.target.contains(dialog)) return;
  dialog.close();
}

button.addEventListener('click', openDialog);
document.addEventListener('click', closeDialog);
dialog {
  padding: 0;
}

.modal {
  display: flex;
  padding: 1rem;
}

dialog::backdrop {
  background-color: rgba(255, 0, 0, 0.25);
}
<p>Click the button to show the dialog.</p>

<button>Show dialog</button>

<p><b>Note:</b> Use the "Esc" button to close the modal.</p>
<p><b>Note:</b> The dialog element is only supported in Chrome 37+, Safari 6+ and Opera 24+.</p>

<dialog id="myDialog">
  <div class="modal">
    <p>This is a dialog window</p>
  </div>
</dialog>

【讨论】:

    【解决方案2】:

    通常,您可以使用 onblur- 或 onfocusout- 事件。当你离开元素 aka 时,两者都会触发。单击另一个元素。 (虽然我还没有专门用对话框测试过它。)

    <dialog onblur="yourFunction()">
    

    【讨论】:

      【解决方案3】:

      我知道这有点不切实际,但你可以添加一个与整个屏幕一样大的 div,当你点击“显示对话框”时,div 的显示是块状的,当你点击大小为屏幕,对话框消失。

      <!DOCTYPE html>
      <html>
      <body>
      
      <p>Click the button to show the dialog.</p>
      
      <button onclick="myFunction()">Show dialog</button>
      
      <p><b>Note:</b> Use the "Esc" button to close the modal.</p>
      <p><b>Note:</b> The dialog element is only supported in Chrome 37+, Safari 6+ and Opera 24+.</p>
      
      <div id="close-dialog-element" onclick="closeDialog()">
          <dialog close id="myDialog">This is a dialog window</dialog>
      </div>
      
      <style>
      #close-dialog-window {
          width: 100vw;
          height: 100vh;
          display: flex;
          align-items: center;
          justify-content: center;
          z-index: 10;
      }
      
      #myDialog {
          z-index: 11;
      }
      </style>
      
      <script>
      function closeDialog() {
          document.getElementById('close-dialog-element').style.display = 'none';
          document.getElementById('myDialog').close();
      }
      
      function myFunction() { 
        document.getElementById("myDialog").showModal(); 
      } 
      </script>
      
      </body>
      </html>

      【讨论】:

        【解决方案4】:

        .showModal() 使用了一个 ::backdrop,它是一个伪元素,它对 DOM 是不可见的,因此是不可触及的。下面的示例显示了如何使用 .show() 方法,以便可以在 &lt;dialog&gt; 之外单击。

        详细信息在示例中进行了注释

        // Reference <dialog> and <button>
        const modal = document.querySelector('.modal');
        const open = document.querySelector('.open');
        
        // Bind click event to <button>
        open.onclick = openModal;
        // Bind click event to <main>
        document.querySelector('main').onclick = closeModal;
        // Bind keydown event close the <dialog> when Esc key is clicked
        document.onkeydown = e => {
          if (e.key === "Escape") {
            closeModal(e);
          }
        }
        
        function openModal(e) {
          // This opens <dialog> with no ::backdrop
          modal.show();
          // A .open to <html> so it appears there's a ::backdrop
          document.documentElement.classList.add('open');
          // Disable <button>
          this.disabled = true;
        }
        
        function closeModal(e) {
          // Reference the tag the user clicked
          const clk = e.target;
          /*
          If the user clicked the <dialog> or anything within the
          <dialog> OR the <button>...
          ... do nothing...
          ... otherwise close <dialog>...
          ... remove .open from <html>...
          ... enable <button>
          */
          if (modal.contains(clk) || clk.matches('.open')) {
            return
          }
          modal.close();
          document.documentElement.classList.remove('open');
          open.disabled = false;
        }
        /* Simulate a ::backdrop */
        :root.open {
          overflow: hidden;
          background-color: rgba(0, 0, 0, 0.2);
        }
        
        /* Cover all of viewport */
        main {
          width: 100%;
          min-height: 100vh;
        }
        
        /* When <dialog> is open <button> should be inert */
        .open .open {
          pointer-events: none;
        }
        <!DOCTYPE html>
        <html>
        
        <body>
          <!-- Wrap everything with <main> -->
          <main>
            <p>Click the button to show the dialog.</p>
        
            <button class='open'>Show Modal</button>
        
            <dialog class="modal">
              <p><b>Note:</b> Use the "Esc" button to close the modal.</p>
              <p><b>Note:</b> Or click outside of modal to close it.</p>
            </dialog>
        
          </main>
        </body>
        
        </html>

        【讨论】:

          【解决方案5】:

          您也可以在这里尝试 Adam Argyle 描述的方法:https://web.dev/building-a-dialog-component/#adding-light-dismiss

          1. 获取对话框元素并向对话框添加事件侦听器 元素
                const dialog = document.querySelector('dialog);
            
                dialog.addEventListener('click', lightDismiss);
            
            1. 创建处理函数并检查对话框元素是否 触发事件。如果是,调用 .close() 方法。这有效 因为,默认情况下,对话框元素会创建一个背景 覆盖页面上的所有其他元素,然后单击那里 === 单击对话框元素本身。这不会影响孩子 对话框中的元素,因此单击那里不会关闭 对话。
                const lightDismiss = ({target:dialog}) => {
                  if (dialog.nodeName === 'DIALOG')
                    dialog.close('dismiss')
                }
            

            顺便推荐一下这篇精彩的dialog tutorial

          【讨论】:

            猜你喜欢
            • 2012-01-13
            • 1970-01-01
            • 1970-01-01
            • 2021-11-24
            • 2012-01-08
            • 1970-01-01
            • 2018-12-02
            • 1970-01-01
            相关资源
            最近更新 更多