Каков наилучший способ отменить распространение событий между вложенными вызовами ng-click?

180

Вот пример. Допустим, я хочу, чтобы изображение было наложено, как на многих сайтах. Поэтому, когда вы щелкаете по миниатюре, над всем окном появляется черный оверлей, и в нем центрируется увеличенная версия изображения. Щелчок по черному наложению отклоняет его; щелчок по изображению вызовет функцию, которая показывает следующее изображение.

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

Проблема в том, что при такой настройке, если щелкнуть изображение, оба nextImage()и hideOverlay()вызываются. Но я хочу, чтобы меня только nextImage()называли.

Я знаю, что вы можете захватить и отменить событие в nextImage()функции, как это:

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

... Но я хочу знать, есть ли лучший способ сделать это на AngularJS, который бы не требовал от меня добавления этого кода ко всем функциям элементов внутри оверлея.

cilphex
источник
1
return falseв конце функции
Джонатан де М.
1
Я считаю, что это способ сделать это onclick, а не ng-click.
Майкл Шепер

Ответы:

193

Что сказал @JosephSilber, или передайте объект $ event в ng-clickcallback и остановите распространение внутри него:

<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
использовать $ event.preventDefault (); в v> 1.5
KoolKabin
3
@KoolKabin Я использую Angular 1.5.8 и $ event.stopPropagation () по-прежнему работает нормально. Однако $ event.preventDefault () не работает
HammerNL
1
Странно, потому что у меня противоположная ситуация. stopPropagation больше не работает, но предотвращаетDefault (). Разница лишь в том, что я звонил прямо по ng-click. <tr ng-click = "ctr.foo (); $ event.preventDefault ()"> ..... </ tr>
MilitelloVinx,
171

Используйте $event.stopPropagation():

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

Вот демонстрация: http://plnkr.co/edit/3Pp3NFbGxy30srl8OBmQ?p=preview

Джозеф Силбер
источник
Я получаю ReferenceError: $event is not definedв консоли.
cilphex
Да, это так ... это также работает, когда я пишу отдельную простую версию с нуля. Я пытаюсь выяснить, что может заставить его не работать в моем коде разработки. Не
знаю
@cilphex - используете ли вы актуальную версию Angular?
Джозеф Сильбер
Да - только что нашел проблему. При тестировании я пытался поставить $event.stopPropagation()в место, где это не работает. Забыл удалить это. Так что даже когда я положил его там, где он работал, его снова вызывали там, где он не работал. Избавился от неисправного дубликата, и это успешно. Спасибо за вашу помощь.
cilphex
101

Мне нравится идея использования директивы для этого:

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

Затем используйте директиву как:

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

Если вы хотите, вы можете сделать это решение более общим, как этот ответ на другой вопрос: https://stackoverflow.com/a/14547223/347216

Дэвид Ипсен
источник
2
Если ваш элемент добавляется / удаляется из DOM динамически, не забывайте следить за вашим элементом на предмет события '$ destroy', чтобы вы могли отсоединить обработчик. Например, element.on ('$ destroy', function () {/ * сделать здесь привязку * /});
broc.seib
такое stop-eventатрибут html? Я не смог найти его ни в Mozilla Developer Network, ни где-либо еще.
Эхтеш Чоудхури
3
@EhteshChoudhury - «stop-event» - это новая директива, которую я создал в приведенном выше фрагменте JS. Узнайте больше об объявлении и использовании директив здесь: docs.angularjs.org/guide/directive
David Ipsen
Отличное решение! Я на самом деле зарегистрировал angular-eat-eventдирективу по беседке, которая работает аналогичным образом!
gion_13
кажется, что это не работает, ng-click оценивает перед директивой. поэтому я могу помешать выполнению директивы, но не наоборот.
Рафаэль
31

Иногда может иметь смысл сделать это:

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

Я решил сделать это таким образом, потому что я не хотел myClickHandler()останавливать распространение события во многих других местах, где оно использовалось.

Конечно, я мог бы добавить логический параметр в функцию-обработчик, но stopPropagation()он гораздо важнее, чем просто true.

Майкл Шепер
источник
2
Мне всегда нравилась эта версия, кажется, вложение элемента не должно
относиться
Пришлось добавить $event.stopPropagation()к внутренним элементам.
Кишор Павар
@KishorPawar: почему? Разве способ, которым я сделал это в ответе, не работал для вас? Если этого не произойдет, было бы хорошо, если бы мы выяснили, происходит ли это из-за изменения в работе Angular или из-за проблемы в вашем коде.
Майкл Шепер
@MichaelScheper Я checkboxзавернул в divэлемент. Мой divпринимает 12 столбцов и checkbox, скажем, 3 столбца. Я хотел вызвать функцию Aпо щелчку divи функцию Bпо щелчку checkbox. поэтому, когда я нажимал, checkboxобе функции вызывались. Когда я добавляю ng-click="$event.stopPropagation()"к checkbox, я получаю только функциюB вызванную .
Кишор Павар
@KishorPawar: Ах. Да, я ожидал бы этого, и действительно, смысл в stopPropagation()том, чтобы остановить событие, распространяющееся на родительские элементы. Я не знаю , как ты называешь , Bтак как это не specififed в ng-click, но суть ответа заключается в том, что вы можете сделать это: <checkbox ng-click="B(); $event.stopPropagation()">. Вам не нужно включать stopPropagation()функцию в B.
Майкл Шепер
7

Это работает для меня:

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

this.doSomething = function($event) {
  $event.stopPropagation();
  $event.preventDefault();
};
Андрей Шатилов
источник
Я хотел обрабатывать щелчки мыши с или без нажатия клавиши Ctrl. Чтобы остановить выделение (и выделение) элементов HTML в браузере (в моем случае IE 11), когда пользователь нажимает различные элементы, удерживая клавишу Ctrl, мне пришлось использовать событие ng-mousedown и отменить поведение по умолчанию с помощью события $ .preventDefault ()
pholpar
4

Если вы не хотите добавлять остановку распространения ко всем ссылкам, это также работает. Чуть более масштабируемый.

$scope.hideOverlay( $event ){

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

}
Хампус Альгрен
источник
2
Это решение работает для предоставленного примера (что здорово!), Однако оно не работает, когда внешний div имеет n или более вложенных элементов до нажатия href / ng-click. Затем, когда вызывается обратный вызов hideOverlay (), целью является один из вложенных элементов, а не самый внешний элемент с директивой ng-click. Для обработки вложенных элементов, возможно, можно использовать jquery, чтобы получить родителей и посмотреть, есть ли у первого родительского элемента, который можно щелкнуть (a, ng-click и т. Д.), Класс оверлея, но это выглядит немного громоздко ...
Амир,
console.log ("potatooverlay" .indexOf ("overlay")! = -1); console.log (/ \ boverlay \ Ъ / g.test ( "potatooverlay"));
Angular позволит вам сделать event.target.classList.indexOf('overlay') И этот трюк отлично работает, даже если div вложен в другие div
deltree
0

Если вы вставите ng-click = "$ event.stopPropagation" в родительский элемент вашего шаблона, то stopPropogation будет перехвачен, поскольку он всплывает в дереве, поэтому вы должны написать его только один раз для всего шаблона.

tbranch
источник
0

Вы можете зарегистрировать другую директиву, ng-clickкоторая изменяет поведение по умолчанию ng-clickи останавливает распространение события. Таким образом, вам не придется добавлять $event.stopPropagationвручную.

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>

Использование контроллера IN

$scope.methodName = function(event) { 
    event.stopPropagation();
}
Динеш Джайн
источник
Сначала отправьте событие в методе, чтобы сделать это
Динеш Джайн
Ваш ответ ничего не добавляет к принятому 4 года назад
Илья Лузянин