简体   繁体   中英

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. 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. 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 .

The 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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