Який найкращий спосіб скасувати поширення події між вкладеними дзвінками ng-клацання?


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 спосіб його виконання, який не вимагає від мене цього префіксу всіх функцій на елементах всередині накладання.


1
return falseнаприкінці функції
Джонатан де М.

1
Я вважаю, що саме так можна зробити onclick, а не ng-click.
Майкл Шепер

Відповіді:


193

Що сказав @JosephSilber, або передайте об'єкт $ події у ng-clickзворотний виклик і зупиніть розповсюдження всередині нього:

<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 більше не працює, але функція prevenDefault () робить. Різниця лише в тому, що я дзвонив безпосередньо на 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 динамічно, не забувайте спостерігати за своїм елементом на події 'знищити', щоб ви могли відв'язати обробник. Напр., Element.on ('$ знищити', функцію () {/ * виконайте розв’язування тут * /});
broc.seib

це stop-eventатрибут HTML? Я не міг знайти його в Мережі розробників Mozilla або деінде.
Ehtesh Choudhury

3
@EhteshChoudhury - "стоп-подія" - нова директива, яку я створив у фрагменті JS вище. Дізнайтеся більше про декларування та використання директив тут: docs.angularjs.org/guide/directive
Девід Іпсен

Приємне рішення! Я фактично зареєстрував angular-eat-eventдирективу про цистерну, яка працює аналогічно!
gion_13

це, здається, не працює, ng-клацання оцінюється перед директивою. тож я можу запобігти виконанню директив, але не навпаки.
Рафаель

31

Іноді це має сенс зробити саме це:

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

Я вирішив зробити це саме так, бо не хотів myClickHandler()зупиняти розповсюдження події у багатьох інших місцях, де це було використано.

Звичайно, я міг би додати булевий параметр до функції обробника, але stopPropagation()це набагато важливіше, ніж просто true.


2
Мені завжди подобалася ця версія, здається, вкладення елемента не повинно бути пов’язане з функцією, яку я викликаю
mcfedr

Довелося додати $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 та скасувати поведінку за замовчуванням через $ event .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. Тоді, коли викликається callOverlay (), ціль - це один із вкладених елементів, а не самий зовнішній елемент із директивою ng-click. Щоб обробити вкладені елементи, можливо, можна використати jquery, щоб отримати батьків і побачити, чи має перший батьківський клік (a, ng-click тощо) клас накладок, але це виглядає трохи громіздко ...
Амір

console.log ("картоплемірка" .indexOf ("накладення")! = -1); console.log (/ \ boverlay \ b / g.test ("картопляна передача"));

кутовий дозволить вам зробити це. Цей event.target.classList.indexOf('overlay') трюк прекрасно працює навіть тоді, коли дів вкладений в інші диви
deltree

0

Якщо ви вставите ng-click = "$ event.stopPropagation" на батьківський елемент вашого шаблону, stopPropogation буде спійманий, коли він розмиває дерево, тому вам потрібно лише один раз написати його для всього шаблону.


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 роки тому
Ілля Лузянін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.