【问题标题】:Angular: event unbinding in directive somehow does not unbind entirelyAngular:指令中的事件解除绑定不知何故并没有完全解除绑定
【发布时间】:2014-01-28 17:50:20
【问题描述】:

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

当发生这种情况时,它会广播一个事件以关闭弹出窗口,并在 $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 的按钮并将其加载到弹出div 中。
  2. 很好地触发 catch-outside-click 指令并在文档上绑定 clickhandler
  3. 在弹出窗口内单击不会执行任何操作(正如在指令中测试过的那样),但在弹出窗口外单击可以很好地关闭弹出窗口,因为它会广播在标头控制器中捕获的事件。
  4. 单击页面中的任意位置不会再次触发该指令,因此我们认为它已正确解除绑定。
  5. 除非您单击按钮再次打开弹出窗口。这似乎既绑定了一个新的文档点击事件,又立即触发了点击处理程序(这让我觉得它触发了第一个点击处理程序)。

目前我不知道如何测试它是触发第一个还是第二个 clickhandler。

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

【问题讨论】:

    标签: javascript angularjs event-handling


    【解决方案1】:

    我们现在可以通过这样做来解决这个问题:

    aOS.directive('catchOutsideClick',
    [
        '$document',
        'eventsService',
        function CatchOutsideClickDirective($document, eventsService) {
    
            ...
    
            setTimeout( function() {
                $document.bind('click', onOutsideClick);
            }, 0);
    
            ...
    
        }
    ]);
    

    看起来好像 setTimeout 中的代码(绑定到点击)在下一个“周期”中执行(尽管超时设置为 0),其中包含模板的点击在同一(上一个)中处理循环作为 catch-click 指令被初始化的地方。至少这是我在调查这个问题时的感受。

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

    【讨论】:

    • 我认为您没有考虑到事件冒泡,因为您绑定到在后代元素的点击处理程序内对文档的点击?你只需要在按钮点击处理程序中调用 event.stopPropagation()
    猜你喜欢
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-05
    • 1970-01-01
    • 2016-02-15
    相关资源
    最近更新 更多