programing

AngularJS 드롭다운 지시문 숨기기(외부를 클릭할 때

instargram 2023. 3. 23. 22:16
반응형

AngularJS 드롭다운 지시문 숨기기(외부를 클릭할 때

확인란과 필터 옵션을 사용하여 다중 선택 드롭다운 목록을 만들려고 합니다.밖을 클릭하여 목록을 숨기려고 하는데 어떻게 하는지 알 수 없습니다.도와주셔서 감사합니다.

http://plnkr.co/edit/tw0hLz68O8ueWj7uZ78c

주의: 솔루션(질문에 제공된 Plunker)은 두 번째 팝업(복수 선택 페이지)을 열 때 다른 상자의 팝업을 닫지 않습니다.

상자를 클릭하여 새 팝업을 열면 클릭 이벤트가 항상 중지됩니다.이벤트는 열린 다른 팝업(닫기 위한 팝업)에 도달하지 않습니다.

을 이문 the the the the the 를 제거함으로써 했다.event.stopPropagation();줄 바꿈과 일치하는 팝업의 모든 하위 요소.

이벤트 요소가 팝업의 하위 요소와 일치하지 않는 경우에만 팝업이 닫힙니다.

디렉티브 코드를 다음과 같이 변경했습니다.

select.delect(모듈 코드)

link: function(scope, element, attr){

    scope.isPopupVisible = false;

    scope.toggleSelect = function(){
        scope.isPopupVisible = !scope.isPopupVisible;
    }

    $(document).bind('click', function(event){
        var isClickedElementChildOfPopup = element
            .find(event.target)
            .length > 0;

        if (isClickedElementChildOfPopup)
            return;

        scope.$apply(function(){
            scope.isPopupVisible = false;
        });
    });
}

당신의 플런커를 포크하여 변경 사항을 적용했습니다.

플런커:바깥쪽에서 클릭 시 팝업 표시 숨기기

스크린샷:

플런커 스크린샷

이것은 오래된 게시물이지만, 이 게시물이 여기 있는 누구에게도 도움이 될 수 있는 경우에 대비하여 모서리 이외에는 의존하지 않는 외부 클릭의 예를 들 수 있습니다.

module('clickOutside', []).directive('clickOutside', function ($document) {

        return {
           restrict: 'A',
           scope: {
               clickOutside: '&'
           },
           link: function (scope, el, attr) {

               $document.on('click', function (e) {
                   if (el !== e.target && !el[0].contains(e.target)) {
                        scope.$apply(function () {
                            scope.$eval(scope.clickOutside);
                        });
                    }
               });
           }
        }

    });

네, 이벤트가 (doc에 따라) 앵귤러 월드 밖에서 일어나고 있기 때문에 $apply()에 전화해야 했습니다.

    element.bind('click', function(event) {
    event.stopPropagation();      
    });

    $document.bind('click', function(){
    scope.isVisible = false;
    scope.$apply();
    });

저는 이런 글로벌 클릭 이벤트를 듣고 깨달았습니다.

.directive('globalEvents', ['News', function(News) {
    // Used for global events
    return function(scope, element) {
        // Listens for a mouse click
        // Need to close drop down menus
        element.bind('click', function(e) {
            News.setClick(e.target);
        });
    }
}])

그런 다음 뉴스 서비스를 통해 이벤트 자체가 방송됩니다.

angular.factory('News', ['$rootScope', function($rootScope) {
    var news = {};
    news.setClick = function( target ) {
        this.clickTarget = target;
        $rootScope.$broadcast('click');
    };
}]);

그런 다음 필요한 장소에서 방송을 들을 수 있습니다.다음은 지시문의 예를 제시하겠습니다.

.directive('dropdown', ['News', function(News) {
  // Drop down menu für the logo button
  return {
    restrict: 'E',
    scope: {},
    link: function(scope, element) {
      var opened = true;
      // Toggles the visibility of the drop down menu
      scope.toggle = function() {
        element.removeClass(opened ? 'closed' : 'opened');
        element.addClass(opened ? 'opened' : 'closed');
      };
      // Listens for the global click event broad-casted by the News service
      scope.$on('click', function() {
        if (element.find(News.clickTarget.tagName)[0] !== News.clickTarget) {
          scope.toggle(false);
        }
      });
      // Init
      scope.toggle();
    }
  }
}])

도움이 됐으면 좋겠네요!

나는 제공된 답변에 완전히 만족하지 못해서 나만의 답변을 만들었다.개선점:

  • 스코프의 보다 방어적인 갱신.적용/삭제가 이미 진행 중인지 확인합니다.
  • 사용자가 이스케이프 키를 누르면 div도 닫힙니다.
  • div가 닫히면 윈도우 이벤트가 바인딩되지 않음(유출)
  • 윈도우 이벤트는 스코프가 파괴되면 언바인드됩니다(유출).

    function link(function link, $syslog, 속성, $syslog) {

    var el = $element[0],
        $$window = angular.element($window);
    
    function onClick(event) {
        console.log('window clicked');
    
        // might need to polyfill node.contains
        if (el.contains(event.target)) {
            console.log('click inside element');
            return;
    
        }
    
        scope.isActive = !scope.isActive;
        if (!scope.$$phase) {
            scope.$apply();
        }
    }
    
    function onKeyUp(event) {
    
        if (event.keyCode !== 27) {
            return;
        }
    
        console.log('escape pressed');
    
        scope.isActive = false;
        if (!scope.$$phase) {
            scope.$apply();
        }
    }
    
    function bindCloseHandler() {
        console.log('binding window click event');
        $$window.on('click', onClick);
        $$window.on('keyup', onKeyUp);
    }
    
    function unbindCloseHandler() {
        console.log('unbinding window click event');
        $$window.off('click', onClick);
        $$window.off('keyup', onKeyUp);
    }
    
    scope.$watch('isActive', function(newValue, oldValue) {
        if (newValue) {
            bindCloseHandler();
        } else {
            unbindCloseHandler();
        }
    });
    
    // prevent leaks - destroy handlers when scope is destroyed
    scope.$on('$destroy', function() {
        unbindCloseHandler();
    });
    

    }

$window링크 기능에 직접 접속합니다.꼭 꼭 이렇게 할 요.$window.

function directive($window) {
    return {
        restrict: 'AE',
        link: function(scope, $element, attributes) {
            link.call(null, scope, $element, attributes, $window);
        }
    };
}

요.angular-click-outside프로젝트에 사용할 수 있습니다.이치노

https://github.com/IamAdamJowett/angular-click-outside

Danny F가 투고한 답변은 훌륭하고 거의 완벽하지만 Th thnh의 코멘트는 옳습니다.따라서 이 디렉티브의 $destroy 이벤트에서 청취자를 삭제하기 위한 수정된 디렉티브는 다음과 같습니다.

const ClickModule = angular
.module('clickOutside', [])
.directive('clickOutside', ['$document', function ($document) {
    return {
        restrict: 'A',
        scope: {
            clickOutside: '&'
        },
        link: function (scope, el, attr) {
            const handler = function (e) {
                if (el !== e.target && !el[0].contains(e.target)) {
                    scope.$apply(function () {
                        console.log("hiiii");
                        //  whatever expression you assign to the click-outside attribute gets executed here
                        //  good for closing dropdowns etc
                        scope.$eval(scope.clickOutside);
                    });
                }
            }

            $document.on('click', handler);

            scope.$on('$destroy', function() {
                $document.off('click', handler);
            });
        }
    }
}]);

핸들러 메서드에 로그를 넣어도 요소가 DOM 에서 삭제되었을 때 로그가 기동하는 것을 확인할 수 있습니다.잔돈을 더하면 잔돈이 없어집니다.남의 눈을 훔치려는 건 아니지만 이건 우아한 해결책에 대한 해결책이야

각도 클릭 아웃사이드 사용

설치:

bower install angular-click-outside --save
npm install @iamadamjowett/angular-click-outside
yarn add @iamadamjowett/angular-click-outside

사용방법:

angular.module('myApp', ['angular-click-outside'])

//in your html
<div class="menu" click-outside="closeThis">
...
</div>

//And then in your controller
$scope.closeThis = function () {
    console.log('closing');
}

https://github.com/IamAdamJowett/angular-click-outside에서 구현에 관한 몇 가지 문제를 발견했습니다.

예를 들어 클릭된 요소가 DOM에서 제거된 경우 위의 지시문에 의해 로직이 트리거합니다.모달에서 클릭 후 요소를 ng-if로 제거하는 논리가 있었기 때문에 나에게는 효과가 없었습니다.

나는 그의 구현을 다시 썼다.배틀테스트는 하지 않았지만 (적어도 내 시나리오에서는) 더 잘 작동하는 것 같다.

angular
  .module('sbs.directives')
  .directive('clickOutside', ['$document', '$parse', '$timeout', clickOutside]);

const MAX_RECURSIONS = 400;

function clickOutside($document, $parse, $timeout) {
  return {
    restrict: 'A',
    link: function ($scope, elem, attr) {
      // postpone linking to next digest to allow for unique id generation
      $timeout(() => {
        function runLogicIfClickedElementIsOutside(e) {
          // check if our element already hidden and abort if so
          if (angular.element(elem).hasClass('ng-hide')) {
            return;
          }

          // if there is no click target, no point going on
          if (!e || !e.target) {
            return;
          }

          let clickedElementIsOutsideDirectiveRoot = false;
          let hasParent = true;
          let recursions = 0;

          let compareNode = elem[0].parentNode;
          while (
            !clickedElementIsOutsideDirectiveRoot &&
            hasParent &&
            recursions < MAX_RECURSIONS
          ) {
            if (e.target === compareNode) {
              clickedElementIsOutsideDirectiveRoot = true;
            }

            compareNode = compareNode.parentNode;
            hasParent = Boolean(compareNode);
            recursions++; // just in case to avoid eternal loop
          }

          if (clickedElementIsOutsideDirectiveRoot) {
            $timeout(function () {
              const fn = $parse(attr['clickOutside']);
              fn($scope, { event: e });
            });
          }
        }

        // if the devices has a touchscreen, listen for this event
        if (_hasTouch()) {
          $document.on('touchstart', function () {
            setTimeout(runLogicIfClickedElementIsOutside);
          });
        }

        // still listen for the click event even if there is touch to cater for touchscreen laptops
        $document.on('click', runLogicIfClickedElementIsOutside);

        // when the scope is destroyed, clean up the documents event handlers as we don't want it hanging around
        $scope.$on('$destroy', function () {
          if (_hasTouch()) {
            $document.off('touchstart', runLogicIfClickedElementIsOutside);
          }

          $document.off('click', runLogicIfClickedElementIsOutside);
        });
      });
    },
  };
}

function _hasTouch() {
  // works on most browsers, IE10/11 and Surface
  return 'ontouchstart' in window || navigator.maxTouchPoints;
}

언급URL : https://stackoverflow.com/questions/14574365/angularjs-dropdown-directive-hide-when-clicking-outside

반응형