Подтвердить что ты не робот

Нажмите везде, но здесь событие

Интересно, как бы я реализовал событие "Click везде, но в этом элементе".

У меня есть кое-что, что вы можете сравнить со списком файлов в проводнике файлов. Вы можете выбрать определенные элементы, но если вы выйдете за пределы элемента управления элемента, ему нужно отменить все.

enter image description here

Добавлен снимок экрана, чтобы сделать его более понятным. Поэтому я хочу, чтобы, если я нажимаю где-нибудь, но на языковых элементах, он должен запускать событие.

Обновление

Чтобы уточнить, я не спрашиваю, как это сделать с помощью jQuery.

4b9b3361

Ответ 1

EDIT: В этом старом, старом ответе было несколько проблем.

* Также: Маркировка сообщества Wiki (нет точек для меня), потому что ошибки

  • N вызывает N использования директивы. Это, вероятно, нежелательно для использования в рамках одной области с соответствующими выражениями.

  • НИЧЕГО НЕ БЫЛО УЧИТЬСЯ С ПОМОЩЬЮ СОБЫТИЙ!!!! ПЛОХО! ПЛОХО! ПЛОХО!

Итак, я обновляю этот ответ. Надеюсь, это не вызвало у кого-то слишком много неприятностей.

Обновленный ответ

Здесь появился новый плункер с этими проблемами... Есть, вероятно, другие вещи, с которыми сталкиваются отдельные разработчики приложений. Это всего лишь пример того, как справиться с этой проблемой.

app.factory('clickAnywhereButHereService', function($document){
  var tracker = [];

  return function($scope, expr) {
    var i, t, len;
    for(i = 0, len = tracker.length; i < len; i++) {
      t = tracker[i];
      if(t.expr === expr && t.scope === $scope) {
        return t;    
      }
    }
    var handler = function() {
      $scope.$apply(expr);
    };

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

    // IMPORTANT! Tear down this event handler when the scope is destroyed.
    $scope.$on('$destroy', function(){
      $document.off('click', handler);
    });

    t = { scope: $scope, expr: expr };
    tracker.push(t);
    return t;
  };
});

app.directive('clickAnywhereButHere', function($document, clickAnywhereButHereService){
  return {
    restrict: 'A',
    link: function(scope, elem, attr, ctrl) {
      var handler = function(e) {
        e.stopPropagation();
      };
      elem.on('click', handler);

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

      clickAnywhereButHereService(scope, attr.clickAnywhereButHere);
    }
  };
});

Оригинальный ответ (с исправлениями для удаления обработчиков событий)

Вы были близки к одному ответу, который вы нашли, но я собрал планку, чтобы показать вам, что он отсутствовал.

app.directive('clickAnywhereButHere', function($document){
  return {
    restrict: 'A',
    link: function(scope, elem, attr, ctrl) {
      var elemClickHandler = function(e) {
        e.stopPropagation();
      };

      var docClickHandler = function() {
        scope.$apply(attr.clickAnywhereButHere);
      };

      elem.on('click', elemClickHandler);
      $document.on('click', docClickHandler);

      // teardown the event handlers when the scope is destroyed.
      scope.$on('$destroy', function() {
        elem.off('click', elemClickHandler);
        $document.off('click', docClickHandler);
      });
    }
  }
})

HTML

<a click-anywhere-but-here="clickedSomewhereElse()" 
  ng-click="clickedHere()">Don't Click Me!</a>

Ответ 2

Проблема с принятым в настоящее время ответом заключается в том, что если вы используете директиву несколько раз, каждый элемент DOM, который имеет прикрепленную директиву, предотвратит "пузырение" (так что если у вас есть два элемента, и вы нажмете на них), обратные вызовы оба будут заблокированы).

РЕДАКТИРОВАТЬ - избегать jQuery, очистить. Определить функцию в своей области и передать ее в эту директиву напрямую (без круглых скобок), и событие будет передано ему при вызове.

app.directive('clickAnywhereButHere', function($document, $parse) {
    return {
        restrict: 'A',
        scope: {
            callback : '=clickAnywhereButHere'
        },
        link: function(scope, element, attr, ctrl) {
            var handler = function(event) {
                if (!element[0].contains(event.target)) {
                    scope.callback(event);
                 }
            };

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

Использование в HTML

<a click-anywhere-but-here="myFunction"></a>

Использование в контроллере

 $scope.myFunction = function (event) { ... } 

-

Обратите внимание, что вам может потребоваться обернуть scope.callback(event) с помощью scope.$apply()

Ответ 3

Если у вас есть много элементов, которым нужна эта директива, вот еще одно решение, оптимизированное по производительности. (Пример списка со 100 + строками, каждый с этой директивой)

Это всегда будет содержать только один приемник $document

angular.module('app').directive('clickElsewhere', ['$document', function ($document) {
return {
  link: function postLink(scope, element, attr) {

    var elsewhere = true;

    element.on('click', function(e) {
      elsewhere = false;
      $document.off('click', clickElsewhere);
      $document.on('click', clickElsewhere);
    });

    var clickElsewhere = function() {
      if (elsewhere) {
        scope.$apply(attr.clickElsewhere);
        $document.off('click', clickElsewhere);
      }
      elsewhere = true;
    };

  }
};
}]);

Проблема с решением от Max Bates заключается в том, что все директивы добавляют слушателя для $document.on('click', function (...)); событие, которое вызывает проблемы с производительностью.

Проблема с принятым ответом указала Макс Бейтс.

Ответ 4

Нашел anwser в этом сообщении .

Директива

app.directive('documentClick', function ($document, $parse) {

  var linkFunction = function ($scope, $element, $attributes) {

    var scopeExpression = $attributes.documentClick;
    var invoker = $parse(scopeExpression);

    $document.on('click', function (event) {

      $scope.$apply(function () {
        invoker($scope, { $event: event });
      });

    });

  };

  return linkFunction;

});

Контроллер:

app.controller('PageCtrl', function ($scope) {

  $scope.click = function (e) {
    if (!$(e.target).is('.language')) {
      //do stuff
    }
  };

});

Вид:

<body ng-controller='PageCtrl' document-click='click($event)'></body>

Ответ 5

Вот вариант, основанный на решении Max, но более естественный с точки зрения стандартных директив событий angular:

app.directive('clickOut', function($document) {
    return {
        restrict: 'A',
        scope: {
            clickOut: '&'
        },
        link: function (scope, element) {
            var handler = function(event) {
                if (!element[0].contains(event.target)) {
                    scope.$apply(function () {
                        scope.clickOut({ $event : event }); 
                    });
                }
            };

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

Использование

<div click-out="myFunction()"></div>

Прохождение события клика

<div click-out="myFunction($event)"></div>

Ответ 6

Самый простой способ - проверить область элемента. $id и clickScope. $id (целевая область события click. $id).

link: function(scope, element, attrs) { 
    //close element when mouse click outside
    var documentClickHandler = function(event) {
        var clickScope = angular.element(event.target).scope();
        if (clickScope.$id != scope.$id) {
            //clickScope.$parent is for clicking on the directive children scope
            if(clickScope.$parent === null || 
               clickScope.$parent.$id != scope.$id){
               //Click everywhere but on this element
               //Do anything you want here, like close your element;
            }
        }

    };

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

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

Ответ 7

Преимущество этого решения:

  • Вам нужно только привязать к $(document). Другое срабатывание событий будет зависеть от $emit событий области видимости.
  • Вы можете использовать оба выражения click-elsewhere="show=false" и click-elsewhere="fn()", благодаря $parse.

код:

// broadcast click event within AppCtrl
app.controller('AppCtrl', function($rootScope) {
  $(document).on('click', function(e) {
    // use $emit so the event stays inside $rootScope
    $rootScope.$emit('click', {target: e.target});
  };
};

app.directive('clickElsewhere', function($rootScope) {
  return {
    restrict: 'A',
    compile: function($element, attr) {
      // store fn in compile so it only execute once
      var fn = $parse(attr['clickElsewhere']); 

      return function(scope, element) {
        var offEvent = $rootScope.$on('click', function(event, target) {
          if ( (element.find($(target)).length) || element.is($(target)) ) return;

          scope.$apply(function() {
            fn(scope, {$event: event});
          });
        });

        scope.$on('$destroy', offEvent);
      };
    }
  };

Использование в HTML:

  • click-elsewhere="fn()"
  • click-elsewhere="show=false"

Ответ 8

возможно, немного из контекста, но вы можете классифицировать (например, "выбранный" ) все выбранные элементы и когда пользователь нажимает на элемент "не нажимать здесь", вы можете рассекретить все элементы, классифицируемые в настоящее время "выбранными" "и классифицировать определенный элемент...