简体   繁体   English

元素接收和丢失类时添加和删除事件侦听器

[英]Add and remove event listener when element receives and loses a class

I have a small search button which expands and displays an input once user clicks on a magnifying glass icon.我有一个小的搜索按钮,一旦用户点击放大镜图标,它就会展开并显示输入。 At this point I am giving my search element the .active class.在这一点上,我给我的搜索元素.active类。 Once a user starts typing, a 'modal' window is shown with results of the search.一旦用户开始输入,就会显示一个带有搜索结果的“模态”窗口。 What I want to be able to do is add and remove my event listener on an outside click (anywhere else apart from the modal window, search button and the input).我想要做的是在外部单击时添加和删除我的事件侦听器(除了模态窗口、搜索按钮和输入之外的任何其他地方)。

Just to clarify all of the above is within the search element so they're all children elements of search.只是为了澄清以上所有内容都在搜索元素中,因此它们都是搜索的子元素。

I have managed to remove the event listener when the element is removed from the DOM but I can't do it so that it is added and removed when the elements receives and loses the .active class.当元素从 DOM 中删除时,我设法删除了事件侦听器,但我无法这样做,以便在元素接收和丢失.active类时添加和删除它。 How can I possibly do that?我怎么可能这样做?

My directive looks as follows.我的指令如下所示。

outsideSearchClick.$inject = ['$document'];

function outsideSearchClick($document) {
  return {
    scope: {
      outsideSearchClick: '&'
    },
    link: function (scope, element, attrs) {
      $document.on('click', handleOutsideClick);

      element.on('$destroy', function() {
        $document.off('click', handleOutsideClick);
      });

      function handleOutsideClick(e) {
        if((element !== e.target || element[0] !== e.target) && !element[0].contains(e.target)) {
          scope.$apply(function () {
            scope.$eval(scope.outsideSearchClick);
          });
        }
      }
    }
  };
}

And my element looks like this alongside with the necessary functions.我的元素与必要的功能一起看起来像这样。

vm.search = {
  opened: false,
  text: ''
};

vm.openSearch = function() {
  vm.search.opened = true;
};

vm.closeSearch = function(e, clearSearch) {
  vm.search.opened = false;

  if(e) {
    e.stopPropagation();
  }

  if(clearSearch) {
    vm.search.text = '';
    vm.searchResults = {};
  }
};
<div class="search" ng-class="{'active': vm.search.opened}"
     ng-click="vm.openSearch()"
     outside-search-click="vm.closeSearch()"
     ng-model-options="{debounce: 500}">
  <i class="fo odo-icon-search"></i>
  <input type="text" ng-model="vm.search.text">

  <div class="search-modal" ng-if="vm.search.opened && vm.search.text">
    <div class="search-modal-header">
      <div class="close-search" ng-click="vm.closeSearch($event, true)">
        <i class="fo odo-icon-cross close-icon"></i>
        <span class="close-text">CLOSE</span>
      </div>
    </div>

    <div class="search-modal-body">
      <!-- BLAH BLAH BLAH -->
    </div>
</div>

I recommend simplifing the directive:我建议简化指令:

documentClick.$inject = ['$document'];

function documentClick($document) {
  return {
    link: function (scope, element, attrs) {
      $document.on('click', handleDocumentClick);

      element.on('$destroy', function() {
        $document.off('click', handleDocumentClick);
      });

      function handleDocumentClick(e) {
          scope.$apply(function () {
            scope.$eval(attrs.documentClick, {$event: e});
          });
      }
    }
  };
}

Notice that the directive injects the event as a local with the identifier $event .请注意,该指令将事件作为具有标识符$event的本地注入。

The HTML: HTML:

<div class="search" ng-class="{'active': vm.search.opened}"
     ng-click="vm.openSearch($event)"
     document-click="vm.closeSearch($event,false)"
     ng-model-options="{debounce: 500}">
  <i class="fo odo-icon-search"></i>
  <input type="text" ng-model="vm.search.text">

  <div class="search-modal" ng-if="vm.search.opened && vm.search.text">
    <div class="search-modal-header">
      <div class="close-search" ng-click="vm.closeSearch($event, true)">
        <i class="fo odo-icon-cross close-icon"></i>
        <span class="close-text">CLOSE</span>
      </div>
    </div>

    <div class="search-modal-body">
      <!-- BLAH BLAH BLAH -->
    </div>
</div>

Instead of adding and removing an event handler, have the handler test for the conditions for which the code should not process the event:不要添加和删除事件处理程序,而是让处理程序测试代码不应处理事件的条件:

vm.openSearch = function(ev) {
  ev.stopPropagation();
  vm.search.opened = true;
};

vm.closeSearch = function(e, clearSearch) {
  if (e.currentTarget == document && !vm.search.opened) {
     //ignore event
     return;
  }

  vm.search.opened = false;    
  if(e) {
    e.stopPropagation();
  }

  if(clearSearch) {
    vm.search.text = '';
    vm.searchResults = {};
  }
};

The avoid the techique of conditionally adding and removing event handlers.避免有条件地添加和删除事件处理程序的技术。 It creates a de facto state which is difficult to understand, debug, test, and maintain.它创建了一个难以理解、调试、测试和维护的事实上的状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM