Angular:指令中的事件取消绑定不会完全取消绑定

时间:2014-01-28 17:50:21

标签: javascript angularjs event-handling

我们有一个显示弹出窗口的指令, 在弹出窗口中,加载了一个模板,其中包含一个捕获文档中所有点击的指令。

当发生这种情况时,它会广播一个事件来关闭弹出窗口,并取消绑定$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>

模板的包含似乎完美无缺,但这是我们现在看到的行为:

  1. 点击切换popupTemplate的按钮,然后将其加载到弹出窗口中。
  2. 可以很好地触发catch-outside-click指令并绑定文档上的clickhandler
  3. 弹出窗口内的单击不执行任何操作(因为在指令中进行了测试),但弹出窗口外的单击可以很好地关闭弹出窗口,因为它会广播在标头控制器中捕获的事件。
  4. 点击页面中的任何位置不会再次触发指令,因此我们认为它已正确解除绑定。
  5. 除了您单击按钮再次打开弹出窗口时。这似乎都绑定了一个新的文档点击事件,但也立即触发了clickhandler(这让我觉得它触发了第一个clickhandler)。
  6. 目前我不知道如何测试是否触发了第一个或第二个点击处理程序。

    任何人都知道如何缩小范围,或者我做错了什么,或者只是指出我如何调试此问题以查看触发了哪个事件处理程序。

1 个答案:

答案 0 :(得分:0)

我们现在能够解决这个问题:

aOS.directive('catchOutsideClick',
[
    '$document',
    'eventsService',
    function CatchOutsideClickDirective($document, eventsService) {

        ...

        setTimeout( function() {
            $document.bind('click', onOutsideClick);
        }, 0);

        ...

    }
]);

看起来setTimeout(绑定到click)中的代码在下一个'循环'中执行(尽管timeout设置为0),其中包含模板的单击处理相同(上一个)循环,即catch-click指令初始化的位置。至少那是我在调查这个问题时得到的感觉。

如果有人能够证实这一点或有更合理的解释(也许是更合理的解决方案),我们仍然渴望学习: - )