单击外部时如何关闭 Angular-bootstrap 弹出窗口

How to close Angular-bootstrap popover when clicking outside

我试图在单击弹出窗口外的任何地方时关闭我的 Angular-bootstrap popover。根据对这个问题的回答,现在可以通过使用新的 popover-is-open 属性来完成(在版本 0.13.4 中):

目前我的 HTML 看起来是这样的:

<div
  ng-click="level.openTogglePopover()"
  popover-template="level.changeLevelTemplate"
  popover-trigger="none"
  popover-placement="right"
  popover-is-open="level.togglePopover">
  <button class="btn btn-default btn-xs" type="button">
    <span class="glyphicon glyphicon-sort"></span>
  </button>
</div>

...以及我的相关控制器代码:

vm.togglePopover = false;

vm.openTogglePopover = function() {
  vm.togglePopover = !vm.togglePopover;
};

这对于 opening/closing 单击上面引用的按钮时的弹出窗口非常有用。我的问题是,如何扩展此功能以在单击弹出框以外的任何位置时关闭弹出框?我将如何设置事件处理来完成此操作?

您需要自己处理事件,因为当您使用新的 *-is-open 属性时,没有事件处理。

如果您不需要对 opening/closing 弹出窗口进行编程控制,那么您可以使用内置的 focus 触发器来满足您的需求。

如果我没理解错的话,您希望当用户单击除弹出框本身内部以外的几乎任何地方时关闭弹出框,实际的关闭按钮除外。这可以通过事件侦听器来完成:

$('html').click(function() {
    if(!$(event.target).is('#foo')) {
        // Code to hide/remove popovers
    }
});

看看这个 plunkr

或者,在您的特定场景中:

$('html').click(function() {
    if(!$(event.target).is('.my-popover-class')) {
        vm.togglePopover = false;
    }
})

首先,如果您希望弹出窗口在 任何 单击时关闭,不仅是弹出窗口之外的那个,您可以使用现有的 UI -Bootstrap代码:

<button class="btn btn-default btn-xs" type="button"
        popover-template="level.changeLevelTemplate"
        popover-trigger="focus"
        popover-placement="right">
  <span class="glyphicon glyphicon-sort"></span>
</button>

这里的技巧是去掉周围的 <div> 并将 popover-trigger="focus" 放在按钮的右边。


如果您只需要在弹出框内容之外的点击时真正关闭弹出框,那将更加困难。您需要一个新指令,例如:

app.directive('clickOutside', function ($parse, $timeout) {
  return {
    link: function (scope, element, attrs) {
      function handler(event) {
        if(!$(event.target).closest(element).length) {
          scope.$apply(function () {
            $parse(attrs.clickOutside)(scope);
          });
        }
      }

      $timeout(function () {
        // Timeout is to prevent the click handler from immediately
        // firing upon opening the popover.
        $(document).on("click", handler);
      });
      scope.$on("$destroy", function () {
        $(document).off("click", handler);
      });
    }
  }
});

然后,在您的弹出框模板中,在最外层元素上使用指令:

<div click-outside="level.closePopover()">
   ... (actual popover content goes here)
</div>

最后,在您的控制器中,实现 closePopover 功能:

vm.closePopover = function () {
  vm.togglePopover = false;
};

我们在这里所做的是:

  • 我们正在监听对文档的任何点击,如果点击发生在我们添加了 close-popover 指令的元素之外:
    • 我们调用任何代码是 close-popover
    • 的值
  • 当指令的范围被销毁时(即当弹出窗口关闭时),我们也会自行清理,这样我们就不再处理点击了。

这不是最干净的解决方案,因为您必须从弹出框模板中调用控制器方法,但这是我想到的最好的解决方案。

close the popover when clicking anywhere outside of the popover

不久前我发现这个答案很有用:How to dismiss a Twitter Bootstrap popover by clicking outside?

我在我的一个演示中使用的代码 (混合 angularjQuery 可能不推荐的事件处理) 是特定于我的需要但是可能会给出一些想法:

  app.directive("eventlistener", function($rootScope) {
    $(window).resize($rootScope.closeAllPopovers); // because Bootstrap popovers don't look good when misplaced

    return {
      link: function(scope, element, attrs) {
        $('body').on('mouseup touchend', $rootScope.closeAllPopovers);
      }
    };
  });

  $rootScope.closeAllPopovers = function (e) {
    $('[data-toggle="popover"]').each(function () {
      if (e) {
        if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
          $(this).popover('hide');
        }
      } else {
        // No event passed - closing all popovers programmatically
        $(this).popover('hide');
      }
    });
  };

我还建议查看以下两者之间的区别:

自 angular-ui 1.0.0 以来,工具提示和弹出窗口有一个新的 outsideClick 触发器(在 this pull request:

中引入
<div
  uib-popover-template="level.changeLevelTemplate"
  popover-trigger="outsideClick"
  popover-placement="right">
  <button class="btn btn-default btn-xs" type="button">
    <span class="glyphicon glyphicon-sort"></span>
  </button>
</div>