Qual è il modo migliore per annullare la propagazione degli eventi tra chiamate ng-click nidificate?


180

Ecco un esempio Diciamo che voglio avere una sovrapposizione di immagini come molti siti. Quindi, quando fai clic su una miniatura, viene visualizzata una sovrapposizione nera su tutta la finestra e una versione più grande dell'immagine viene centrata in essa. Facendo clic sulla sovrapposizione nera la si chiude; facendo clic sull'immagine verrà chiamata una funzione che mostra l'immagine successiva.

Il codice HTML:

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

Il 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
    }
}

Il problema è che con questa configurazione, se si fa clic sull'immagine, entrambi nextImage()e hideOverlay()vengono chiamati. Ma quello che voglio è solo nextImage()per essere chiamato.

So che puoi catturare e cancellare l'evento nella nextImage()funzione in questo modo:

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

... Ma voglio sapere se esiste un modo migliore di AngularJS per farlo che non mi richiederà di aggiungere un prefisso a tutte le funzioni sugli elementi all'interno della sovrapposizione con questo frammento.


1
return falseal termine della funzione
Jonathan de M.

1
Credo sia questo il modo di farlo onclick, no ng-click.
Michael Scheper,

Risposte:


193

Cosa ha detto @JosephSilber, oppure passa l'oggetto $ event al ng-clickcallback e ferma la propagazione al suo interno:

<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
usa $ event.preventDefault (); in v> 1.5
KoolKabin il

3
@KoolKabin Sto usando Angular 1.5.8 e $ event.stopPropagation () funziona ancora bene. $ event.preventDefault () tuttavia, non funziona
HammerNL

1
Strano, perché ho la situazione opposta. stopPropagation non funziona più, ma preventDefault () funziona. L'unica differenza è che ho chiamato direttamente su ng-click. <tr ng-click = "ctr.foo (); $ event.preventDefault ()"> ..... </tr>
MilitelloVinx il

171

Utilizzare $event.stopPropagation():

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

Ecco una demo: http://plnkr.co/edit/3Pp3NFbGxy30srl8OBmQ?p=preview


Salgo ReferenceError: $event is not definedsulla console.
cilphex,

Sì, funziona ... funziona anche quando scrivo una versione semplice separata da zero. Sto cercando di capire cosa potrebbe non funzionare nel mio codice di sviluppo. Non so: \
cilphex

@cilphex - Stai utilizzando una versione aggiornata di Angular?
Joseph Silber,

Sì, ho appena riscontrato il problema. Durante i test ho provato a mettere $event.stopPropagation()in un posto dove non funziona. Hai dimenticato di rimuoverlo. Quindi, anche quando l'ho messo dove funzionava, veniva chiamato una seconda volta dove non funzionava. Si è sbarazzato del duplicato disfunzionale e ha successo. Grazie per l'aiuto.
cilphex,

101

Mi piace l'idea di utilizzare una direttiva per questo:

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

Quindi utilizzare la direttiva come:

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

Se lo desideri, potresti rendere questa soluzione più generica come questa risposta a una domanda diversa: https://stackoverflow.com/a/14547223/347216


2
Se il tuo elemento viene aggiunto / rimosso dal DOM in modo dinamico, non dimenticare di guardare il tuo elemento per un evento "$ destroy" in modo da poter separare il gestore. Ad esempio, element.on ('$ destroy', function () {/ * do the unbinding qui * /});
broc.seib,

è stop-eventun attributo html? Non sono riuscito a trovarlo su Mozilla Developer Network o altrove.
Ehtesh Choudhury,

3
@EhteshChoudhury - "stop-event" è la nuova direttiva che ho creato nello snippet JS sopra. Scopri di più su come dichiarare e utilizzare le direttive qui: docs.angularjs.org/guide/directive
David Ipsen,

Bella soluzione! In realtà ho registrato la angular-eat-eventdirettiva sul pergolato, che funziona in modo simile!
gion_13,

non sembra funzionare, ng-click valuta prima della direttiva. così posso impedire l'esecuzione della direttiva ma non viceversa.
Rafael,

31

A volte, può avere più senso solo fare questo:

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

Ho scelto di farlo in questo modo perché non volevo myClickHandler()interrompere la propagazione degli eventi in molti altri luoghi in cui è stata utilizzata.

Certo, avrei potuto aggiungere un parametro booleano alla funzione del gestore, ma stopPropagation()è molto più significativo del semplice true.


2
Mi è sempre piaciuta questa versione, sembra che l'annidamento di element non sia correlato alla funzione che sto chiamando
mcfedr,

Ho dovuto aggiungere $event.stopPropagation()elementi interni.
Kishor Pawar,

@KishorPawar: Why? Il modo in cui l'ho fatto nella risposta non ha funzionato per te? In caso contrario, sarebbe utile per noi scoprire se è a causa di un cambiamento nel modo in cui funziona Angular o di un problema nel codice.
Michael Scheper,

@MichaelScheper Sto avendo checkboxavvolto divnell'elemento. Il mio divsta prendendo 12 colonne e checkboxsta dicendo dire 3 colonne. Volevo chiamare una funzione Aal clic di dive funzione Bal clic di checkbox. quindi quando stavo facendo clic su checkboxentrambe le funzioni venivano invocate. Quando aggiungo ng-click="$event.stopPropagation()"a checkbox, ho solo Binvocato la funzione .
Kishor Pawar,

@KishorPawar: Ah. Sì, mi aspetterei questo, e in effetti il ​​punto stopPropagation()è fermare la propagazione dell'evento agli elementi padre. Non sono sicuro di come si sta chiamando Bdal momento che non è specififed in ng-click, ma il punto della risposta è che si può fare questo: <checkbox ng-click="B(); $event.stopPropagation()">. Non è necessario includere la stopPropagation()funzione in B.
Michael Scheper,

7

Questo funziona per me:

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

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

Volevo gestire i clic del mouse con o senza premere il tasto Ctrl. Per interrompere la selezione (ed evidenziare) degli elementi HTML nel browser (IE 11 nel mio caso) quando l'utente fa clic su vari elementi tenendo premuto il tasto Ctrl, ho dovuto utilizzare l'evento ng-mousedown e annullare il comportamento predefinito tramite $ event .preventDefault ()
pholpar

4

Se non si desidera aggiungere la propagazione di interruzione a tutti i collegamenti, funziona anche questo. Un po 'più scalabile.

$scope.hideOverlay( $event ){

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

}

2
Questa soluzione funziona per l'esempio fornito (il che è fantastico!), Tuttavia non funziona quando il div esterno ha n o più elementi nidificati prima del clic href / ng. Quindi, quando viene richiamato il callback hideOverlay (), la destinazione è uno degli elementi nidificati e non l'elemento più esterno con una direttiva ng-click. Per gestire gli elementi nidificati è possibile usare jquery per ottenere i genitori e vedere se il primo genitore cliccabile (a, ng-click, ecc.) Avesse la classe overlay, ma sembra un po 'ingombrante ...
Amir,

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

angular ti lascerà fare event.target.classList.indexOf('overlay') e quel trucco funziona benissimo anche quando il div è annidato in altri div
deltree

0

Se inserisci ng-click = "$ event.stopPropagation" sull'elemento principale del tuo modello, stopPropogation verrà catturato mentre bolle sull'albero, quindi devi scriverlo solo una volta per l'intero modello.


0

È possibile registrare un'altra direttiva in cima alla ng-clickquale modifica il comportamento predefinito ng-clicke interrompe la propagazione dell'evento. In questo modo non dovresti aggiungere $event.stopPropagationmanualmente.

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 controller IN

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

Prima invia l'evento con il metodo per farlo
Dinesh Jain,

La tua risposta non aggiunge nulla a quella accettata 4 anni fa
Ilya Luzyanin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.