Нажмите везде, но здесь событие
Интересно, как бы я реализовал событие "Click везде, но в этом элементе".
У меня есть кое-что, что вы можете сравнить со списком файлов в проводнике файлов. Вы можете выбрать определенные элементы, но если вы выйдете за пределы элемента управления элемента, ему нужно отменить все.
![enter image description here]()
Добавлен снимок экрана, чтобы сделать его более понятным. Поэтому я хочу, чтобы, если я нажимаю где-нибудь, но на языковых элементах, он должен запускать событие.
Обновление
Чтобы уточнить, я не спрашиваю, как это сделать с помощью jQuery.
Ответы
Ответ 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
возможно, немного из контекста, но вы можете классифицировать (например, "выбранный" ) все выбранные элементы и когда пользователь нажимает на элемент "не нажимать здесь", вы можете рассекретить все элементы, классифицируемые в настоящее время "выбранными" "и классифицировать определенный элемент...