Wie kann die Ereignisweitergabe zwischen verschachtelten ng-click-Aufrufen am besten abgebrochen werden?

180

Hier ist ein Beispiel. Angenommen, ich möchte eine Bildüberlagerung wie bei vielen Websites. Wenn Sie also auf eine Miniaturansicht klicken, wird über Ihrem gesamten Fenster eine schwarze Überlagerung angezeigt, in der eine größere Version des Bildes zentriert ist. Durch Klicken auf das schwarze Overlay wird es geschlossen. Durch Klicken auf das Bild wird eine Funktion aufgerufen, die das nächste Bild anzeigt.

Das HTML:

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

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

Das Problem ist, dass bei diesem Setup, wenn Sie auf das Bild klicken, beide nextImage()und hideOverlay()aufgerufen werden. Aber ich möchte nur nextImage()angerufen werden.

Ich weiß, dass Sie das Ereignis in der folgenden nextImage()Funktion erfassen und abbrechen können:

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

... Aber ich möchte wissen, ob es eine bessere AngularJS-Methode gibt, bei der ich nicht alle Funktionen von Elementen innerhalb des Overlays mit diesem Snippet voranstellen muss.

Cilphex
quelle
1
return falseam Ende der Veranstaltung
Jonathan de M.
1
Ich glaube, das ist der Weg onclick, nicht ng-click.
Michael Scheper

Antworten:

193

Was @JosephSilber gesagt hat, oder übergeben Sie das $ event-Objekt an einen ng-clickRückruf und stoppen Sie die Weitergabe darin:

<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
}
Stewie
quelle
8
benutze $ event.preventDefault (); in v> 1.5
KoolKabin
3
@KoolKabin Ich verwende Angular 1.5.8 und $ event.stopPropagation () funktioniert immer noch einwandfrei. $ event.preventDefault () funktioniert jedoch nicht
HammerNL
1
Seltsam, weil ich die gegenteilige Situation habe. stopPropagation funktioniert nicht mehr, präventDefault () jedoch. Der einzige Unterschied ist, dass ich direkt auf ng-click angerufen habe. <tr ng-click = "ctr.foo (); $ event.preventDefault ()"> ..... </ tr>
MilitelloVinx
171

Verwendung $event.stopPropagation():

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

Hier ist eine Demo: http://plnkr.co/edit/3Pp3NFbGxy30srl8OBmQ?p=preview

Joseph Silber
quelle
Ich steige ReferenceError: $event is not definedin die Konsole.
Cilphex
Ja, das funktioniert ... es funktioniert auch, wenn ich eine separate einfache Version von Grund auf neu schreibe. Ich versuche herauszufinden, warum es in meinem Entwicklungscode möglicherweise nicht funktioniert. Keine Ahnung: \
cilphex
@cilphex - Verwenden Sie eine aktuelle Version von Angular?
Joseph Silber
Ja - habe gerade das Problem gefunden. Beim Testen habe ich versucht, $event.stopPropagation()an einem Ort zu platzieren, an dem es nicht funktioniert. Ich habe vergessen, es zu entfernen. Selbst wenn ich es dort platzierte, wo es funktionierte, wurde es ein zweites Mal aufgerufen, wo es nicht funktionierte. Das dysfunktionale Duplikat wurde entfernt und es ist erfolgreich. Danke für Ihre Hilfe.
Cilphex
101

Ich mag die Idee, eine Richtlinie dafür zu verwenden:

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

Verwenden Sie dann die Direktive wie folgt:

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

Wenn Sie möchten, können Sie diese Lösung allgemeiner gestalten, wie diese Antwort auf eine andere Frage: https://stackoverflow.com/a/14547223/347216

David Ipsen
quelle
2
Wenn Ihr Element dynamisch zum DOM hinzugefügt / daraus entfernt wird, vergessen Sie nicht, Ihr Element auf ein '$ destroy'-Ereignis zu überwachen, damit Sie den Handler aufheben können. ZB element.on ('$ destroy', function () {/ * hier die Entbindung vornehmen * /});
Broc.seib
ist stop-eventein HTML-Attribut? Ich konnte es weder im Mozilla Developer Network noch anderswo finden.
Ehtesh Choudhury
3
@EhteshChoudhury - "stop-event" ist die neue Direktive, die ich im obigen JS-Snippet erstellt habe. Weitere Informationen zum
David Ipsen
Schöne Lösung! Ich habe tatsächlich die angular-eat-eventRichtlinie über Laube registriert, die auf ähnliche Weise funktioniert!
gion_13
es scheint nicht zu funktionieren, ng-click wertet vor der richtlinie aus. So kann ich verhindern, dass die Direktive ausgeführt wird, aber nicht umgekehrt.
Rafael
31

Manchmal ist es am sinnvollsten, dies einfach zu tun:

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

Ich habe mich dafür entschieden, weil ich myClickHandler()die Ereignisausbreitung an den vielen anderen Orten, an denen sie verwendet wurde, nicht stoppen wollte .

Sicher, ich hätte der Handlerfunktion einen booleschen Parameter hinzufügen können, aber das stopPropagation()ist viel aussagekräftiger als nur true.

Michael Scheper
quelle
2
Ich mochte diese Version immer, anscheinend sollte die Verschachtelung des Elements nicht mit der Funktion zusammenhängen, die ich
aufrufe
Ich musste $event.stopPropagation()interne Elemente hinzufügen .
Kishor Pawar
@ KishorPawar: Warum? Hat die Art und Weise, wie ich es in der Antwort getan habe, bei Ihnen nicht funktioniert? Wenn dies nicht der Fall wäre, wäre es gut für uns herauszufinden, ob dies auf eine Änderung der Funktionsweise von Angular oder auf ein Problem in Ihrem Code zurückzuführen ist.
Michael Scheper
@ MichaelScheper Ich habe checkboxin divElement eingewickelt . Mein divnimmt 12 Spalten und checkboxnimmt beispielsweise 3 Spalten. Ich wollte eine Funktion aufzurufen , Aauf klicken divund die Funktion Bder auf Klick checkbox. Als ich darauf klickte, wurden checkboxbeide Funktionen aufgerufen. Als ich hinzufügen ng-click="$event.stopPropagation()"zu checkbox, habe ich nur die Funktion Baufgerufen.
Kishor Pawar
@ KishorPawar: Ah. Ja, ich würde dies erwarten, und in der Tat geht stopPropagation()es darum, die Ausbreitung des Ereignisses auf übergeordnete Elemente zu stoppen. Ich bin nicht sicher, wie Sie anrufen, Bda es nicht angegeben ist ng-click, aber der Sinn der Antwort ist, dass Sie dies tun können : <checkbox ng-click="B(); $event.stopPropagation()">. Sie müssen die stopPropagation()Funktion nicht einschließen B.
Michael Scheper
7

Das funktioniert bei mir:

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

this.doSomething = function($event) {
  $event.stopPropagation();
  $event.preventDefault();
};
Andrey Shatilov
quelle
Ich wollte Mausklicks mit oder ohne Drücken der Strg-Taste verarbeiten. Um die Auswahl (und Hervorhebung) von HTML-Elementen im Browser (in meinem Fall IE 11) zu stoppen, wenn der Benutzer bei gedrückter Strg-Taste auf verschiedene Elemente klickt, musste ich das Ereignis ng-mousedown verwenden und das Standardverhalten über $ event abbrechen .preventDefault ()
Pholpar
4

Wenn Sie nicht möchten, dass die Stop-Weitergabe zu allen Links hinzugefügt wird, funktioniert dies ebenfalls. Ein bisschen skalierbarer.

$scope.hideOverlay( $event ){

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

}
Hampus Ahlgren
quelle
2
Diese Lösung funktioniert für das bereitgestellte Beispiel (was großartig ist!), Funktioniert jedoch nicht, wenn das äußere Div vor dem href / ng-Klick n oder mehr verschachtelte Elemente enthält. Wenn dann der Rückruf von hideOverlay () aufgerufen wird, ist das Ziel eines der verschachtelten Elemente und nicht das äußerste Element mit einer ng-click-Direktive. Um möglicherweise mit verschachtelten Elementen umzugehen, könnte man jquery verwenden, um die Eltern zu ermitteln und festzustellen, ob der erste klickbare Elternteil (a, ng-click usw.) die Überlagerungsklasse hatte, aber das sieht etwas umständlich aus ...
Amir
console.log ("Kartoffelüberlagerung" .indexOf ("Überlagerung")! = -1); console.log (/ \ boverlay \ b / g.test ("Kartoffelüberlagerung"));
eckig lässt dich tun event.target.classList.indexOf('overlay') Und dieser Trick funktioniert auch dann gut, wenn der Div in anderen Divs verschachtelt ist
Deltree
0

Wenn Sie ng-click = "$ event.stopPropagation" in das übergeordnete Element Ihrer Vorlage einfügen, wird stopPropogation beim Sprudeln des Baums abgefangen, sodass Sie es nur einmal für Ihre gesamte Vorlage schreiben müssen.

tbranch
quelle
0

Sie können eine weitere Direktive registrieren, ng-clickdie das Standardverhalten von ändert ng-clickund die Ereignisausbreitung stoppt. Auf diese Weise müssten Sie nicht $event.stopPropagationvon Hand hinzufügen .

app.directive('ngClick', function() {
    return {
        restrict: 'A',
        compile: function($element, attr) {
            return function(scope, element, attr) {
                element.on('click', function(event) {
                    event.stopPropagation();
                });
            };
        }
    }
});
bert
quelle
0
<div ng-click="methodName(event)"></div>

IN Controller verwenden

$scope.methodName = function(event) { 
    event.stopPropagation();
}
Dinesh Jain
quelle
Senden Sie zuerst ein Ereignis in der Methode, um es zu tun
Dinesh Jain
Ihre Antwort fügt nichts zu der vor 4 Jahren akzeptierten hinzu
Ilya Luzyanin