【问题标题】:How to correctly watch for clicks outside an element?如何正确观察元素外的点击?
【发布时间】:2015-05-04 15:23:18
【问题描述】:

当我单击“添加新项目”按钮时,我希望显示一个弹出表单。 然后,如果我在此表单之外单击,我希望它隐藏自己。 要显示/隐藏表单,我使用 ng-show 指令。为了观察外部点击,我使用第三方 Angular 指令 angular-clickout 但有一个问题 - 该指令开始在页面加载时工作,当我点击“添加新项目”按钮时,它会立即调用我的关闭函数,该函数反过来设置布尔值属性为 false 值,ng-show 隐藏表单...

HTML:

<button ng-click="vm.displayDialogAddNewItem()>Add New Item</button>
<div class="new-item-dialog" 
     ng-show="vm.dialogAddNewItemIsVisible"     
     click-out="vm.hideDialogAddNewItem()">
    ... omitted code ...
</div>

控制器代码:

vm.displayDialogAddNewItem = function() {
    vm.dialogAddNewItemIsVisible = true;
};

vm.hideDialogAddNewItem = function() {
    vm.dialogAddNewItemIsVisible = false;
};

【问题讨论】:

标签: javascript angularjs angularjs-ng-click ng-show


【解决方案1】:

您需要防止事件在 DOM 树中冒泡,从而防止父处理程序(在窗口上,关闭模式)收到事件通知。

<!-- Pass the $event to the handler -->
<button ng-click="vm.displayDialogAddNewItem($event)>Add New Item</button>

JS

vm.displayDialogAddNewItem = function($event) {
    vm.dialogAddNewItemIsVisible = true;
    $event.stopPropagation(); // Stop bubbling up.
};

【讨论】:

【解决方案2】:

这与事件在 JavaScript 中的工作方式有关。

假设你有这个 HTML:

<div id="content">
  <input type="button />
</div>

如果单击按钮,则会生成单击事件。该事件将从捕获阶段开始(自上而下),它将从window 开始,然后是div#content,然后是按钮。然后冒泡阶段开始(自下而上),首先是按钮,然后是 div#content,然后是 window

默认情况下,事件附加到冒泡阶段,因此您的按钮接收点击事件,显示弹出窗口,然后窗口接收点击并隐藏弹出窗口。这有点不方便。

解决此问题的一种方法是使用stopPropagation 阻止事件冒泡。这种方法存在一些问题。例如,如果您有 2 个可以产生弹出窗口的按钮。点击 button1 然后 button2 不会关闭第一个弹出窗口,因为共享父级永远不会看到任何点击事件。

另一种解决方案是在捕获阶段添加关闭弹出窗口。当您想要阻止对弹出窗口本身的点击关闭弹出窗口时,这里会出现一个潜在问题。这个块在捕获阶段很难确定。另一个解决方案是在捕获阶段标记事件,并延迟关闭到冒泡阶段(允许弹出窗口抛出一个块)。但是,这与 stopPropagation 不匹配。任何带有stopPropagation 的元素都会导致弹出窗口更接近失败,因为该事件将永远不会再次冒泡。

另一种方法是让弹出处理程序知道它应该忽略下一次点击。 stopPropagation 再次有可能在这里造成损害。比如id#content上面有stopPropagation

您还可以使用某种setTimeout 技巧来延迟弹出窗口的呈现,直到点击事件完成它的冒泡阶段。这行得通,但感觉就像一个可怕的黑客攻击。


正如您从这个相当详尽的答案中看到的那样,这是我自己遇到的一个问题,并且已经考虑了很多。我还不知道解决方案。每个解决方案似乎都有它的问题。然而,我已经决定 stopPropagation 不好。我的理由是stopPropagation 打破了模块化。子节点可以在没有父节点同意或不知情的情况下影响父节点的行为,这似乎是个问题。

【讨论】:

  • “单击 button1 然后 button2 不会关闭第一个弹出窗口,因为单击事件已中止。” 仅当 button2 是 button1 的父级时才如此,这不太可能。
  • @Blaise 请记住,用于检测元素外部点击的点击事件必须在某处的父元素上,并且最好尽可能高。因此,如果他们共享此父级(可能是body),则第一个弹出窗口不会关闭。所以我会说这非常可能。
  • clickout 使用window 作为共享父级。见:github.com/neoziro/angular-clickout/blob/master/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
相关资源
最近更新 更多