如何通过单击 ::backdrop 来关闭新的 html <dialog> 标签

Posted

技术标签:

【中文标题】如何通过单击 ::backdrop 来关闭新的 html <dialog> 标签【英文标题】:How to close the new html <dialog> tag by clicking on its ::backdrop 【发布时间】:2014-11-09 22:28:49 【问题描述】:

我没有找到任何内置的解决方案或解决方法来通过单击其背景 (::backdrop) 来关闭 html5 元素,尽管它显然是一个基本功能。

【问题讨论】:

【参考方案1】:

如果您使用click,即使您从对话框的内部拖动到外部,它也会关闭对话框,这可能很烦人。

您可以使用pointerdownpointerup 更准确地检测外部点击。

// Close a dialog when the user clicks outside of it
// Not using click because form submit can trigger click on submit button,
// and also it would close if you drag from within the dialog to the outside
// (e.g. if you're selecting text, or perhaps you start clicking the close button and reconsider)
function clickOutsideToClose(dialog) 
    function isOutsideDialog(event) 
        const rect = dialog.getBoundingClientRect();
        return (
            event.clientX < rect.left ||
            event.clientX > rect.right ||
            event.clientY < rect.top ||
            event.clientY > rect.bottom
        );
    
    addEventListener("pointerdown", function (event) 
        if (event.target !== dialog) 
            return;
        
        if (isOutsideDialog(event)) 
            addEventListener("pointerup", function (event) 
                if (isOutsideDialog(event)) 
                    closeSettings();
                
            ,  once: true );
        
    );

到目前为止,我还没有尝试过 div 内容包装方法。也许这也解决了这个问题,或者可以解决这个问题?

【讨论】:

【参考方案2】:

@Seralo 答案的简化版本是:

dialog.addEventListener("click", event => 
    const rect = dialog.getBoundingClientRect();
    if (event.clientY < rect.top || event.clientY > rect.bottom ||
        event.clientX < rect.left || event.clientX > rect.right) 
        dialog.close();
    
;

【讨论】:

【参考方案3】:

另外一个类似于其他人提到的wrapped div方法的简单方法是给对话框本身padding: 0(比如Chrome倾向于给对话框填充),并在对话框中添加点击事件。 form 元素无论如何都会获得差异,因此不需要额外的 div。 我注意到在上面的任何示例中都没有使用该表单,所以我认为这是值得投入使用的,因为它是在对话框中使用按钮时的标准的一部分。

<dialog style="padding: 0; border: 0;">
  <form method="dialog">
    <button>Click here</button>
  </form>
</dialog>

<script>
function onClick(event) 
  if (event.target === dialog) 
    dialog.close();
  


const dialog = document.querySelector("dialog");
dialog.addEventListener("click", onClick);
dialog.showModal();
</script>

Demo on CodePen

【讨论】:

这将适用于覆盖对话框的任何元素;干得好,包括符合规范的表格。【参考方案4】:

我想出了一个非常简单的解决方案来解决这个问题。在&lt;dialog&gt; 中添加一个 div 容器。然后检查这是否是整个事情的父母。如果不是,就是对话框本身,可以关闭。

HTML

<dialog id="favDialog">
  <div class="test">
  Hello
  </div>
</dialog>

JS

document.querySelector('dialog').addEventListener('click', function(e) 
  if(!e.target.closest('div')) 
    e.target.close();
  
);

【讨论】:

这是一个非常好的解决方案,因为它引用了对话框本身,而不是让我编写 ID 150 X 引用的其他代码。【参考方案5】:

对于遇到此问题并希望遵循@meaku 推荐的解决方案的任何人,以下是我如何解决它以使用 a 封装元素而不使用 getBoundingClientRect() 计算:

const popup = document.getElementById('popup');
const popupDialog = document.getElementById('popup-dialog');
popup.addEventListener('click', function(e)
  console.info(e.target.tagName);
  if (e.target.tagName === 'DIALOG') popupDialog.close()
);
<div id="popup" style="padding: 0">
    <dialog id="popup-dialog" style="display:none;">
        <h4>Dialog Title</h4>
        <footer class="modal-footer">
            <button id="popup-close" type="button">close</button>
            <button id="popup-ok" type="button">ok</button>
        </footer>
    </dialog>
</div>

【讨论】:

这不是@meaku 推荐的。【参考方案6】:

另一个更有效的解决方案是将dialog-content 包装在带有padding: 0div 中。这样,您可以检查单击事件的event.target,它在背景的情况下引用对话框,在实际模式的情况下引用div 中的任何其他元素。

通过不检查实际尺寸,我们可以防止布局循环。

【讨论】:

【参考方案7】:

可以使用对话框边界矩形检测背景点击。

var dialog = document.getElementByTagName('dialog');
dialog.showModal();
dialog.addEventListener('click', function (event) 
    var rect = dialog.getBoundingClientRect();
    var isInDialog=(rect.top <= event.clientY && event.clientY <= rect.top + rect.height
      && rect.left <= event.clientX && event.clientX <= rect.left + rect.width);
    if (!isInDialog) 
        dialog.close();
    
);

【讨论】:

我还必须检查event.targetdialog。例如if (!isInDialog &amp;&amp; event.target.tagName === 'DIALOG')。如果通过点击“enter”在对话框中提交表单,则单击事件事件将从提交按钮冒泡,并且由于某种原因,它的 clientX 和 clientY 为 0。 @kmurph79 在下面试试我的答案 - 它不需要检查边界客户矩形。 有什么理由不用rect.rightrect.bottom 而不是rect.left + rect.widthrect.top + rect.height

以上是关于如何通过单击 ::backdrop 来关闭新的 html <dialog> 标签的主要内容,如果未能解决你的问题,请参考以下文章

单击关闭时保持 Bootstrap 下拉菜单打开

如何通过单击菜单外部来关闭移动菜单

如何通过单击对话框外部来关闭对话框?

如何通过单击外部来关闭 Twitter Bootstrap 弹出窗口?

模态框-如何设置使得点击遮罩层不会关闭模态框

如何通过单击栏按钮关闭弹出框