我们有一个指令显示弹出窗口,在弹出窗口中,加载了模板,并带有一个指令,该指令捕获文档中的所有单击。
发生这种情况时,它会广播一个事件以关闭弹出窗口,并在 $document
上解开事件。
这是捕获文档点击的指令。
aOS.directive('catchOutsideClick',
[
'$document',
'eventsService',
function CatchOutsideClickDirective($document, eventsService) {
var CatchClick = function CatchClick(scope, element, attrs) {
// Check if there was clicked on parent or one of its children
function isDescendant(parent, child) {
var node = child.parentNode;
while (node !== null) {
if (node === parent) {
return true;
}
node = node.parentNode;
}
return false;
}
// Done clicking
var onOutsideClick = function onOutsideClick(event) {
var clickedItem = event.target;
if (!isDescendant(element[0], clickedItem) && element[0] !== clickedItem) {
eventsService.publish('clicked-outside-popup', element[0]);
$document.unbind('click', onOutsideClick);
}
};
// Catch clicks!
$document.bind('click', onOutsideClick);
};
return {
restrict: 'A',
scope: false,
link: CatchClick
};
}
]
);
这是加载弹出窗口的HTML:
<div id="header" ng-controller="framework.header">
<div data-ng-include="popupTemplate"></div>
</div>
为了简单起见,让我们声明控制器framework.header
将$scope.popupTemplate
切换到下面显示的HTML的路径:
<div ng-controller="user.profle" catch-outside-click>
<!-- loads of content -->
</div>
模板的包含似乎可以很好地工作,但是这是我们现在看到的行为:
- 您单击切换
popupTemplate
的按钮,然后将其加载到弹出窗口中。 - 可以很好地发射赶上旁边单击指令,并在文档上绑定单击器
- 弹出窗口内部的单击无济于事(在指令中对此进行了测试),但是弹出窗口外的单击弹出窗口很好地关闭了弹出窗口,这是因为它广播了捕获在Header Controller中的事件。 。
- 单击页面中的任何地方不会再次触发指令,因此我们认为它是正确的。
- 除非您单击按钮再次打开弹出窗口。这似乎都绑定了一个新文档点击事件,但也立即触发了clickhandler(这使我认为它触发了第一个ClickHandler)。
我目前不知道如何测试它是触发第一个还是第二个点击手。
任何人都知道我如何缩小这个问题,要么我在做完全错误的事情,要么指向我如何调试此问题以查看触发哪个EventHandler。
我们现在能够解决此问题:
aOS.directive('catchOutsideClick',
[
'$document',
'eventsService',
function CatchOutsideClickDirective($document, eventsService) {
...
setTimeout( function() {
$document.bind('click', onOutsideClick);
}, 0);
...
}
]);
看起来settimeout内部的代码(绑定到点击)在下一个"周期"中执行(尽管超时设置为0),在其中单击以包含模板在同一(以前)中处理模板循环作为接口指令初始化的位置。至少在调查这个问题时我会感到这种感觉。
如果有人可以确认或具有更合理的解释(也许是更合乎逻辑的解决方案),我们仍然渴望学习: - )