Estava imaginando como eu implementaria um evento “Clique em todos os lugares, mas neste elemento”.
Eu tenho algo que você pode comparar a uma lista de arquivos em um explorador de arquivos. Você pode selecionar certos elementos, mas se você clicar fora do controlador do elemento, ele precisará desmarcar tudo.
Adicionado uma captura de canvas para torná-lo mais claro. Então, o que eu quero fazer é que, se eu clicar em qualquer lugar, mas nos elementos de linguagem, ele deve triggersr um evento.
Atualizar
Para esclarecer, não estou perguntando como posso fazer isso com jQuery.
* Também: Marking Community Wiki (sem pontos para mim) porque erros
N pede N usos da diretiva. Isso provavelmente não é desejável para usos dentro do mesmo escopo com expressões correspondentes.
NADA estava USANDO OS MANIPULADORES DO EVENTO !!!! MAU! MAU! MAU!
Então, estou atualizando esta resposta. Espero que isso não tenha causado muitos problemas.
Aqui está um novo plunker com esses problemas corrigidos … há provavelmente outras coisas que os desenvolvedores de aplicativos individuais irão encontrar. Este é apenas um exemplo de como lidar com esse problema.
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); } }; });
Você estava perto com a única resposta que encontrou, mas eu juntei um plunk para você mostrar o que estava faltando.
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
Don't Click Me!
O problema com a resposta atual aceita é que se você usar a diretiva várias vezes, todo elemento DOM que tiver a diretiva anexada evitará o borbulhamento (portanto, se você tiver em dois elementos e clicar em um deles, os retornos de chamada de ambos serão bloqueado).
EDIT – evite jQuery, clean up – Defina uma function em seu escopo e passe-a diretamente para esta diretiva (sem parênteses), e o evento será passado para ela quando chamado.
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); }); } } });
Uso em HTML
Uso no Controlador
$scope.myFunction = function (event) { ... }
–
Observe que você pode precisar scope.callback(event)
com o scope.$apply()
Se você tiver muitos elementos que precisam desta diretiva, aqui está outra solução otimizada para desempenho. (Exemplo de uma lista com mais de 100 linhas, cada uma com esta diretiva)
Isso sempre conterá apenas um ouvinte de $ 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; }; } }; }]);
O problema com a solução do Max Bates é que todas as diretivas estão adicionando um ouvinte para o $ document.on (‘click’, function (…)); evento que causa problemas de desempenho.
Problema com resposta aceita tem Max Bates allready afirmou.
Encontrei a resposta nesta postagem do blog .
Directiva:
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; });
Controlador:
app.controller('PageCtrl', function ($scope) { $scope.click = function (e) { if (!$(e.target).is('.language')) { //do stuff } }; });
Visão:
Aqui está uma variante baseada na solução de Max, mas mais natural em termos de diretivas de events angulares padrão:
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); }); } }; });
Uso
Evento de clique de passagem
A maneira mais simples é verificar o escopo do elemento: $ id e o clickScope. $ Id (o escopo de destino do evento 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); }); }
$(document)
. Outro disparo de events dependeria do $emit
de events de escopo. click-elsewhere="show=false"
e click-elsewhere="fn()"
, graças a $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); }; } };
click-elsewhere="fn()"
click-elsewhere="show=false"
é possivelmente um pouco fora do contexto, mas você pode classificar (com por exemplo “selecionado”) todos os itens selecionados e quando um usuário clica no elemento ‘não clique aqui’, você pode desclassificar todos os itens atualmente classificados como “selecionados” classifique o elemento específico …