jQuery的单击事件内停止底层的传播NG-点击 [英] Stop propagation of underlying ng-click inside a jQuery click event

查看:318
本文介绍了jQuery的单击事件内停止底层的传播NG-点击的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个Twitter的引导下拉菜单嵌套在 TR 里面。在 TR 是通过点击 NG-点击。点击页面上的任何地方将面临崩溃的下拉菜单。这种行为是指令通过 $ document.bind定义(点击,closeMenu)

A Twitter Bootstrap dropdown is nested inside a tr. The tr is clickable through ng-click. Clicking anywhere on the page will collapse the dropdown menu. That behavior is defined in a directive through $document.bind('click', closeMenu).

所以,当菜单被打开,用户点击一排,我希望菜单关闭(因为它),我想prevent该行的单击事件。

的jsfiddle: http://jsfiddle.net/LMc2f/1/ 结果
的jsfiddle的 +指令内联 http://jsfiddle.net/9DM8U/1/

JSFiddle : http://jsfiddle.net/LMc2f/1/
JSFiddle + directive inline : http://jsfiddle.net/9DM8U/1/

从UI的引导,第三方物流企业,0.10.0.js相关code:

Relevant code from ui-bootstrap-tpls-0.10.0.js :

angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
  var openElement = null,
      closeMenu   = angular.noop;
  return {
    restrict: 'CA',
    link: function(scope, element, attrs) {
      scope.$watch('$location.path', function() { closeMenu(); });
      element.parent().bind('click', function() { closeMenu(); });
      element.bind('click', function (event) {

        var elementWasOpen = (element === openElement);

        event.preventDefault();
        event.stopPropagation();

        if (!!openElement) {
          closeMenu();
        }

        if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
          element.parent().addClass('open');
          openElement = element;
          closeMenu = function (event) {
            if (event) {
              event.preventDefault();
              event.stopPropagation();
            }
            $document.unbind('click', closeMenu);
            element.parent().removeClass('open');
            closeMenu = angular.noop;
            openElement = null;
          };
          $document.bind('click', closeMenu);
        }
      });
    }
  };
}]);

我无法弄清楚如何停止在 closeMenu 标的 NG-点击事件。

请注意:我不能找到一种方法来访问 $事件,所以我一直没能尝试 $ event.stopPropagation()

NOTE : I can't find a way to access $event so I haven't been able to try $event.stopPropagation().

推荐答案

在下拉菜单指令结合的文档上的click事件,但是当你点击该行,该事件的开始从目标元素向下传播以根文档节点( D - > TR - > - > 文件)。

The dropdown directive binds the click event on the document, but when you click on the row, the event starts propagating from the target element down to the root document node (td -> tr -> table -> document).

所以这就是为什么你的 NG-点击的处理程序,你有你的行中,总是被调用,即使指令串门的文件点击泡沫。

So that's why your ng-click handler, that you have on your row, always gets called, even though the directive is "stopping" the bubble on document click.

解决方案是文档添加单击处理程序时使用将useCapture 标记。

Solution is to use the useCapture flag when adding the click handler for the document.

启动捕获后,所有指定类型的事件将是
  被分派到任何之前寄发予注册的监听器
  在DOM树EVENTTARGET它的下方。 mdn

After initiating capture, all events of the specified type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. mdn

现在,指示下拉指令使用自己的处理程序,您需要更改指令的来源。但它是一个第三方的指令,你可能不希望这样做,因为maintability的原因。

Now, to instruct the dropdown directive to use your own handler, you need to change the source of the directive. But it's a third party directive, and you probably don't want to do that, for maintability reasons.

这是在哪里。你可以用$装饰来改变即时的第三方模块的源,但并未实际接触实际的源文件的功能强大的角$装饰踢。

This is where the powerful angular $decorator kicks in. You may use the $decorator to change the source of the third-party module on-the-fly, without actually touching the actual source files.

因此​​,随着装饰到位,并与文档节点上自定义事件处理程序,这是你如何能作出这样的行为下拉菜单:

So, with decorator in place, and with custom event handler on the document node, this is how you can make that dropdown behave:

<大骨节病> FIDDLE

var myApp = angular.module('myApp', []);

/**
 * Original dropdownToggle directive from ui-bootstrap.
 * Nothing changed here.
 */
myApp.directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
  var openElement = null,
      closeMenu   = angular.noop;
  return {
    restrict: 'CA',
    link: function(scope, element, attrs) {
      scope.$watch('$location.path', function() { closeMenu(); });
      element.parent().bind('click', function() { closeMenu(); });
      element.bind('click', function (event) {

        var elementWasOpen = (element === openElement);

        event.preventDefault();
        event.stopPropagation();

        if (!!openElement) {
          closeMenu();
        }

        if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
          element.parent().addClass('open');
          openElement = element;
          closeMenu = function (event) {
            if (event) {
              event.preventDefault();
              event.stopPropagation();
            }
            $document.unbind('click', closeMenu);
            element.parent().removeClass('open');
            closeMenu = angular.noop;
            openElement = null;
          };
          $document.bind('click', closeMenu); /* <--- CAUSE OF ALL PROBLEMS ----- */
        }
      });
    }
  };
}]);


/**
 * This is were we decorate the dropdownToggle directive
 * in order to change the way the document click handler works
 */
myApp.config(function($provide){
  'use strict';

  $provide.decorator('dropdownToggleDirective', [
      '$delegate',
      '$document',
      function ($delegate, $document) {

        var directive = $delegate[0];
        var openElement = null;
        var closeMenu = angular.noop;

        function handler(e){
            var elm = angular.element(e.target);
          if(!elm.parents('.dropdown-menu').length){
            e.stopPropagation();
            e.preventDefault();
          }
          closeMenu();
          // After closing the menu, we remove the all-seeing handler
          // to allow the application click events to work nnormally
          $document[0].removeEventListener('click', handler, true);
        }

        directive.compile = function(){
          return function(scope, element) {
            scope.$watch('$location.path', closeMenu);
            element.parent().bind('click', closeMenu);
            element.bind('click', function (event) {

              var elementWasOpen = (element === openElement);

              event.preventDefault();
              event.stopPropagation();

              if (!!openElement) {
                closeMenu();
              }

              if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
                element.parent().addClass('open');
                openElement = element;
                closeMenu = function (event) {
                  if (event) {
                    event.preventDefault();
                    event.stopPropagation();
                  }
                  $document.unbind('click', closeMenu);
                  element.parent().removeClass('open');
                  closeMenu = angular.noop;
                  openElement = null;
                };


                // We attach the click handler by specifying the third "useCapture" parameter as true
                $document[0].addEventListener('click', handler, true);
              }
            });
          };
        };

        return $delegate;
      }
  ]);

});

更新:

请注意,更新后的自定义处理程序将prevent冒泡除非目标元素是实际的下拉选项。这将解决这个问题,在单击事件是在下拉选项,当点击prevented均匀。

Note that the updated custom handler will prevent bubbling unless the target element is the actual drop-down option. This will solve the problem where click event was being prevented even when clicking on the drop-down options.

这仍然不会prevent事件泡倒排(从下拉选项),但是这东西是不相关的下拉指令的任何方式。无论如何,以prevent这种冒泡可以通过 $事件对象 NG-点击前pression功能,使用该对象停止甚至泡到表行:

This still won't prevent the event to bubble down to row (from drop-down option), but this is something that's not in any way related to the drop-down directive. Anyway, to prevent such bubbling you can pass the $event object to ng-click expression function and use that object to stop the even to bubble down to the table row:

<div ng-controller="DropdownCtrl">
  <table>
    <tr ng-click="clicked('row')">
      <td>

        <div class="btn-group">
          <button type="button" class="btn btn-default dropdown-toggle">
            Action <span class="caret"></span>
          </button>
          <ul class="dropdown-menu" role="menu">
            <li ng-repeat="choice in items">
              <a ng-click="clicked('link element', $event)">{{choice}}</a>
            </li>
          </ul>
        </div>

      </td>
    </tr>
  </table>
</div>

function DropdownCtrl($scope) {
  $scope.items = [
    "Action",
    "Another action",
    "Something else here"
  ];

  $scope.clicked = function(what, event) {
    alert(what + ' clicked');
    if(event){
      event.stopPropagation();
      event.preventDefault();
    }
  }

}

这篇关于jQuery的单击事件内停止底层的传播NG-点击的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆