¿Cuál es la mejor manera de cancelar la propagación de eventos entre llamadas ng-click anidadas?


180

Aquí hay un ejemplo. Digamos que quiero tener una superposición de imágenes como muchos sitios. Por lo tanto, cuando hace clic en una miniatura, aparece una superposición negra sobre toda la ventana y se centra una versión más grande de la imagen. Al hacer clic en la superposición negra, se descarta; Al hacer clic en la imagen, se llamará a una función que muestra la siguiente imagen.

El html:

<div ng-controller="OverlayCtrl" class="overlay" ng-click="hideOverlay()">
    <img src="http://some_src" ng-click="nextImage()"/>
</div>

El javascript:

function OverlayCtrl($scope) {
    $scope.hideOverlay = function() {
        // Some code to hdie the overlay
    }
    $scope.nextImage = function() {
        // Some code to find and display the next image
    }
}

El problema es que con esta configuración, si hace clic en la imagen, tanto nextImage()y hideOverlay()recibe una llamada. Pero lo que quiero es que solo nextImage()se me llame.

Sé que puedes capturar y cancelar el evento en la nextImage()función de esta manera:

if (window.event) {
    window.event.stopPropagation();
}

... Pero quiero saber si hay una mejor manera de hacerlo de AngularJS que no requiera que prefije todas las funciones de los elementos dentro de la superposición con este fragmento.


1
return falseal final de la función
Jonathan de M.

1
Creo que esa es la forma de hacerlo onclick, no ng-click.
Michael Scheper

Respuestas:


193

Lo que dijo @JosephSilber, o pasar el objeto $ event a la ng-clickdevolución de llamada y detener la propagación dentro de él:

<div ng-controller="OverlayCtrl" class="overlay" ng-click="hideOverlay()">
  <img src="http://some_src" ng-click="nextImage($event)"/>
</div>
$scope.nextImage = function($event) {
  $event.stopPropagation();
  // Some code to find and display the next image
}

8
use $ event.preventDefault (); en v> 1.5
KoolKabin

3
@ KoolKabin Estoy usando Angular 1.5.8 y $ event.stopPropagation () todavía funciona bien. $ event.preventDefault () sin embargo, no funciona
HammerNL

1
Extraño, porque tengo la situación opuesta. stopPropagation ya no funciona, pero preventDefault () sí. La única diferencia es que llamé directamente en ng-click. <tr ng-click = "ctr.foo (); $ event.preventDefault ()"> ..... </tr>
MilitelloVinx

171

Uso $event.stopPropagation():

<div ng-controller="OverlayCtrl" class="overlay" ng-click="hideOverlay()">
    <img src="http://some_src" ng-click="nextImage(); $event.stopPropagation()" />
</div>

Aquí hay una demostración: http://plnkr.co/edit/3Pp3NFbGxy30srl8OBmQ?p=preview


Me meto ReferenceError: $event is not defineden la consola.
cilphex

Sí, lo hace ... también funciona cuando escribo una versión simple separada desde cero. Estoy tratando de descubrir qué podría hacer que no funcione en mi código de desarrollo. No sé: \
cilphex

@cilphex - ¿Está utilizando una versión actualizada de Angular?
Joseph Silber

Sí, acabo de encontrar el problema. Cuando probé intenté ponerlo $event.stopPropagation()en un lugar donde no funciona. Olvidé eliminarlo. Entonces, incluso cuando lo puse donde funcionó, se lo llamó por segunda vez donde no funcionó. Se deshizo del duplicado disfuncional y es exitoso. Gracias por tu ayuda.
cilphex

101

Me gusta la idea de usar una directiva para esto:

.directive('stopEvent', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            element.bind('click', function (e) {
                e.stopPropagation();
            });
        }
    };
 });

Luego use la directiva como:

<div ng-controller="OverlayCtrl" class="overlay" ng-click="hideOverlay()">
    <img src="http://some_src" ng-click="nextImage()" stop-event/>
</div>

Si lo desea, puede hacer que esta solución sea más genérica como esta respuesta a una pregunta diferente: https://stackoverflow.com/a/14547223/347216


2
Si su elemento se agrega / elimina del DOM dinámicamente, no olvide mirar su elemento para un evento '$ destroy' para que pueda desvincular el controlador. Por ejemplo, element.on ('$ destroy', function () {/ * hacer la desvinculación aquí * /});
broc.seib

es stop-eventun atributo html? No pude encontrarlo en Mozilla Developer Network o en ningún otro lugar.
Ehtesh Choudhury

3
@EhteshChoudhury - "stop-event" es la nueva directiva que creé en el fragmento de JS anterior. Vea más sobre declarar y usar directivas aquí: docs.angularjs.org/guide/directive
David Ipsen

Buena solución! De hecho, registré la angular-eat-eventdirectiva sobre Bower, que funciona de manera similar.
gion_13

no parece funcionar, ng-click evalúa antes de la directiva. entonces puedo evitar que la directiva se ejecute pero no al revés.
Rafael

31

A veces, puede tener más sentido hacer esto:

<widget ng-click="myClickHandler(); $event.stopPropagation()"/>

Elegí hacerlo de esta manera porque no quería myClickHandler()detener la propagación del evento en los muchos otros lugares donde se usaba.

Claro, yo podría haber añadido un parámetro booleano a la función de controlador, pero stopPropagation()es mucho más significativo que simplemente true.


2
Siempre me gustó esta versión, parece que la anidación del elemento no debería estar relacionada con la función que estoy llamando
mcfedr

Tuve que agregar $event.stopPropagation()elementos internos.
Kishor Pawar

@KishorPawar: ¿Por qué? ¿La forma en que lo hice en la respuesta no funcionó para usted? Si no fuera así, sería bueno para nosotros averiguar si se debe a un cambio en el funcionamiento de Angular o a un problema en su código.
Michael Scheper

@MichaelScheper Estoy checkboxenvuelto en divelemento. Mi divtoma 12 columnas y checkboxtoma 3 columnas. Quería llamar a una función Aal hacer clic divy a la función Bal hacer clic checkbox. así que cuando estaba haciendo clic en checkboxambas funciones se invocaban. Cuando agrego ng-click="$event.stopPropagation()"a checkbox, solo se Binvoca la función .
Kishor Pawar

@KishorPawar: Ah. Sí, esperaría esto, y de hecho, el punto stopPropagation()es detener la propagación del evento a los elementos principales. No estoy seguro de cómo lo está llamando Bya que no está en specififed ng-click, pero el punto de la respuesta es que se puede hacer esto: <checkbox ng-click="B(); $event.stopPropagation()">. No tiene que incluir la stopPropagation()función en B.
Michael Scheper

7

Esto funciona para mi:

<a href="" ng-click="doSomething($event)">Action</a>

this.doSomething = function($event) {
  $event.stopPropagation();
  $event.preventDefault();
};

Quería manejar los clics del mouse con o sin presionar la tecla Ctrl. Para detener la selección (y resaltar) de elementos HTML en el navegador (IE 11 en mi caso) cuando el usuario hace clic en varios elementos manteniendo presionada la tecla Ctrl, tuve que usar el evento ng-mousedown y cancelar el comportamiento predeterminado a través de $ event .preventDefault ()
pholpar

4

Si no desea tener que agregar la detención de propagación a todos los enlaces, esto también funciona. Un poco más escalable.

$scope.hideOverlay( $event ){

   // only hide the overlay if we click on the actual div
   if( $event.target.className.indexOf('overlay') ) 
      // hide overlay logic

}

2
Esta solución funciona para el ejemplo proporcionado (¡lo cual es genial!), Sin embargo, no funciona cuando el div externo tiene n o más elementos anidados antes del clic href / ng. Luego, cuando se invoca la devolución de llamada hideOverlay (), el destino es uno de los elementos anidados y no el elemento más externo con una directiva ng-click. Para manejar los elementos anidados concebible que uno podría usar jQuery para llegar a los padres y ver si el primero se puede hacer clic (a, NG-clic, etc.) de los padres tenía la clase de superposición, pero que se parece un poco engorroso ...
Amir

console.log ("potatooverlay" .indexOf ("overlay")! = -1); console.log (/ \ boverlay \ b / g.test ("potatooverlay"));

angular te permitirá hacer event.target.classList.indexOf('overlay') y ese truco funciona bien incluso cuando el div está anidado en otros divs
deltree

0

Si inserta ng-click = "$ event.stopPropagation" en el elemento principal de su plantilla, se detendrá stopPropogation a medida que burbujea en el árbol, por lo que solo tiene que escribirlo una vez para toda la plantilla.


0

Puede registrar otra directiva además de la ng-clickcual modifica el comportamiento predeterminado ng-clicky detiene la propagación del evento. De esta manera no tendría que agregar $event.stopPropagationa mano.

app.directive('ngClick', function() {
    return {
        restrict: 'A',
        compile: function($element, attr) {
            return function(scope, element, attr) {
                element.on('click', function(event) {
                    event.stopPropagation();
                });
            };
        }
    }
});

0
<div ng-click="methodName(event)"></div>

Uso del controlador IN

$scope.methodName = function(event) { 
    event.stopPropagation();
}

Primer evento de envío en el método para hacerlo
Dinesh Jain

Su respuesta no agrega nada al aceptado hace 4 años
Ilya Luzyanin
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.