Jaki jest najlepszy sposób na anulowanie propagacji zdarzeń między zagnieżdżonymi wywołaniami ng-click?

180

Oto przykład. Powiedzmy, że chcę mieć nakładkę graficzną podobną do wielu witryn. Tak więc po kliknięciu miniatury na całym oknie pojawia się czarna nakładka, w której jest umieszczona większa wersja obrazu. Kliknięcie czarnej nakładki powoduje jej oddalenie; kliknięcie obrazu wywoła funkcję, która pokazuje następny obraz.

HTML:

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

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

Problemem jest to, że przy tej konfiguracji, jeśli klikniesz obraz, zarówno nextImage()i hideOverlay()są nazywane. Ale chcę tylko nextImage()zostać powołany.

Wiem, że możesz przechwytywać i anulować zdarzenie w następującej nextImage()funkcji:

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

... Ale chcę wiedzieć, czy jest lepszy sposób AngularJS, który nie wymaga ode mnie prefiksu wszystkich funkcji w elementach nakładki za pomocą tego fragmentu.

cilphex
źródło
1
return falsena końcu funkcji
Jonathan de M.
1
Uważam, że tak to się robi onclick, nie ng-click.
Michael Scheper,

Odpowiedzi:

193

Co powiedział @JosephSilber, lub przekaż obiekt zdarzenia $ do ng-clickwywołania zwrotnego i zatrzymaj propagację w nim:

<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
źródło
8
użyj $ event.preventDefault (); in v> 1.5
KoolKabin
3
@KoolKabin Używam Angular 1.5.8 i $ event.stopPropagation () nadal działa dobrze. $ event.preventDefault () jednak nie działa
HammerNL
1
Dziwne, bo mam odwrotną sytuację. Funkcja stopPropagation już nie działa, ale funkcja zapobieganiaDefault () działa. Jedyną różnicą jest to, że zadzwoniłem bezpośrednio na ng-click. <tr ng-click = "ctr.foo (); $ event.preventDefault ()"> ..... </tr>
MilitelloVinx
171

Użyj $event.stopPropagation():

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

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

Joseph Silber
źródło
Wchodzę ReferenceError: $event is not defineddo konsoli.
cilphex
Tak, działa ... działa również, gdy piszę osobną prostą wersję od zera. Próbuję dowiedzieć się, co może sprawić, że nie będzie działać w moim kodzie programistycznym. Nie
wiem
@cilphex - Czy używasz aktualnej wersji Angular?
Joseph Silber
Tak - właśnie znalazłem problem. Podczas testowania próbowałem umieścić $event.stopPropagation()w miejscu, w którym to nie działa. Zapomniałem go usunąć. Więc nawet kiedy położyłem to tam, gdzie zadziałało, było to nazywane po raz drugi tam, gdzie nie zadziałało. Pozbyłem się dysfunkcyjnego duplikatu i udało się. Dzięki za pomoc.
cilphex
101

Podoba mi się pomysł użycia do tego dyrektywy:

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

Następnie użyj dyrektywy w następujący sposób:

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

Jeśli chcesz, możesz uczynić to rozwiązanie bardziej ogólnym, na przykład odpowiedź na inne pytanie: https://stackoverflow.com/a/14547223/347216

David Ipsen
źródło
2
Jeśli Twój element jest dodawany / usuwany dynamicznie z DOM, nie zapomnij obserwować go pod kątem zdarzenia „$ destroy”, abyś mógł odłączyć program obsługi. Np. Element.on ('$ destroy', function () {/ * wykonaj unbinding tutaj * /});
broc.seib
jest stop-eventatrybutem HTML? Nie mogłem go znaleźć w Mozilla Developer Network ani nigdzie indziej.
Ehtesh Choudhury,
3
@EhteshChoudhury - „stop-event” to nowa dyrektywa, którą utworzyłem w powyższym fragmencie JS. Dowiedz się więcej o deklarowaniu i stosowaniu dyrektyw tutaj: docs.quarejs.org/guide/directive
David Ipsen
Fajne rozwiązanie! Właściwie zarejestrowałem angular-eat-eventdyrektywę o altanie, która działa w podobny sposób!
gion_13,
wydaje się, że to nie działa, ng-click ocenia przed dyrektywą. więc mogę zapobiec wykonywaniu dyrektywy, ale nie odwrotnie.
Rafael
31

Czasami może to mieć sens:

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

Postanowiłem to zrobić w ten sposób, ponieważ nie chciałem myClickHandler()zatrzymywać propagacji zdarzeń w wielu innych miejscach, w których były używane.

Jasne, mogłem dodać parametr boolowski do funkcji obsługi, ale stopPropagation()jest on o wiele bardziej znaczący niż tylko true.

Michael Scheper
źródło
2
Zawsze podobała mi się ta wersja, wydaje się, że zagnieżdżanie elementu nie powinno być powiązane z funkcją, którą
wywołuję
Musiałem dodać $event.stopPropagation()do elementów wewnętrznych.
Kishor Pawar
@KishorPawar: Dlaczego? Czy sposób, w jaki to zrobiłem w odpowiedzi, nie zadziałał dla ciebie? Jeśli tak się nie stanie, dobrze byłoby nam dowiedzieć się, czy jest to spowodowane zmianą sposobu działania Angulara, czy problemem w twoim kodzie.
Michael Scheper
@MichaelScheper checkboxOwinąłem divelement. Mój divbierze 12 kolumn i checkboxbierze powiedzmy 3 kolumny. Chciałem wywołać funkcję Apo kliknięciu divi funkcję Bpo kliknięciu checkbox. więc kiedy kliknąłem, checkboxobie funkcje były wywoływane. Kiedy dodam ng-click="$event.stopPropagation()"do checkbox, wywołano tylko funkcję B.
Kishor Pawar
@KishorPawar: Ah. Tak, oczekiwałbym tego i rzeczywiście chodzi o stopPropagation()to, aby zatrzymać propagację zdarzenia do elementów nadrzędnych. Nie jestem pewien, jak dzwonisz B, ponieważ nie jest w specififed ng-click, ale punkt odpowiedź jest taka, że można to zrobić: <checkbox ng-click="B(); $event.stopPropagation()">. Nie musisz dołączać tej stopPropagation()funkcji do B.
Michael Scheper
7

To działa dla mnie:

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

this.doSomething = function($event) {
  $event.stopPropagation();
  $event.preventDefault();
};
Andrey Shatilov
źródło
Chciałem obsługiwać kliknięcia myszy za pomocą klawisza Ctrl lub bez niego. Aby zatrzymać zaznaczanie (i wyróżnianie) elementów HTML w przeglądarce (w moim przypadku IE 11), gdy użytkownik kliknie różne elementy, przytrzymując klawisz Ctrl, musiałem użyć zdarzenia ng-mousedown i anulować domyślne zachowanie za pomocą $ event .preventDefault ()
pholpar
4

Jeśli nie chcesz dodawać zatrzymania propagacji do wszystkich łączy, to również działa. Trochę bardziej skalowalny.

$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
źródło
2
To rozwiązanie działa na podanym przykładzie (co jest świetne!), Ale nie działa, gdy zewnętrzny div ma n lub więcej zagnieżdżonych elementów przed kliknięciem href / ng. Następnie, gdy wywoływane jest wywołanie zwrotne hideOverlay (), celem jest jeden z zagnieżdżonych elementów, a nie najbardziej zewnętrzny element z dyrektywą ng-click. Aby sobie poradzić z zagnieżdżonymi elementami, można użyć jquery, aby uzyskać rodziców i sprawdzić, czy pierwszy klikalny (a, ng-kliknięcie itp.) Rodzic ma klasę nakładki, ale wygląda to trochę nieporęcznie ...
Amir
console.log („potatooverlay” .indexOf („overlay”)! = -1); console.log (/ \ boverlay \ b / g.test („potatooverlay”));
kątowy pozwoli ci to zrobić event.target.classList.indexOf('overlay') I ta sztuczka działa dobrze, nawet jeśli div jest zagnieżdżony w innych
divach
0

Jeśli wstawisz ng-click = "$ event.stopPropagation" na elemencie nadrzędnym twojego szablonu, stopPropogation zostanie przechwycone, gdy pojawi się ono w drzewie, więc musisz napisać go tylko raz dla całego szablonu.

tbranch
źródło
0

Możesz zarejestrować kolejną dyrektywę, ng-clickktóra zmienia domyślne zachowanie ng-clicki zatrzymuje propagację zdarzeń. W ten sposób nie musisz dodawać $event.stopPropagationręcznie.

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

Wykorzystanie kontrolera IN

$scope.methodName = function(event) { 
    event.stopPropagation();
}
Dinesh Jain
źródło
Najpierw wyślij zdarzenie w metodzie, aby to zrobić
Dinesh Jain
Twoja odpowiedź nic nie dodaje do przyjętej 4 lata temu
Ilya Luzyanin