Cách tốt nhất để hủy lan truyền sự kiện giữa các cuộc gọi ng-click lồng nhau là gì?


180

Đây là một ví dụ. Giả sử tôi muốn có một lớp phủ hình ảnh giống như nhiều trang web. Vì vậy, khi bạn nhấp vào hình thu nhỏ, lớp phủ màu đen sẽ xuất hiện trên toàn bộ cửa sổ của bạn và một phiên bản lớn hơn của hình ảnh được đặt ở giữa. Nhấp vào lớp phủ màu đen sẽ loại bỏ nó; nhấp vào hình ảnh sẽ gọi một chức năng hiển thị hình ảnh tiếp theo.

Các html:

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

Các 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
    }
}

Vấn đề là với thiết lập này, nếu bạn nhấp vào hình ảnh, cả hai nextImage()hideOverlay()được gọi. Nhưng những gì tôi muốn là chỉ nextImage()được gọi.

Tôi biết bạn có thể chụp và hủy sự kiện trong nextImage()chức năng như thế này:

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

... Nhưng tôi muốn biết liệu có cách nào tốt hơn để thực hiện AngularJS không yêu cầu tôi phải thêm tiền tố vào tất cả các chức năng trên các phần tử bên trong lớp phủ với đoạn mã này.


1
return falseở cuối chức năng
Jonathan de M.

1
Tôi tin rằng đó là cách để làm điều đó onclickchứ không phải ng-click.
Michael Scheper

Câu trả lời:


193

Những gì @JosephSilber đã nói, hoặc chuyển đối tượng sự kiện $ vào ng-clickcuộc gọi lại và dừng việc truyền bá bên trong nó:

<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
sử dụng $ event.preventDefault (); trong v> 1.5
KoolKabin

3
@KoolKabin Tôi đang sử dụng Angular 1.5.8 và $ event.stopPropagation () vẫn hoạt động tốt. $ event.preventDefault () tuy nhiên, không hoạt động
HammerNL

1
Thật lạ, vì tôi có hoàn cảnh ngược lại. stopPropagation không hoạt động nữa, nhưng ngăn chặnDefault () thì có. Chỉ khác là tôi gọi trực tiếp trên ng-click. <tr ng-click = "ctr.foo (); $ event.preventDefault ()"> ..... </ tr>
MilitelloVinx

171

Sử dụng $event.stopPropagation():

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

Đây là bản demo: http://plnkr.co/edit/3Pp3NFbGxy30srl8OBmQ?p=preview


Tôi ReferenceError: $event is not definedvào trong bàn điều khiển.
cilphex

Vâng, nó ... nó cũng hoạt động khi tôi viết một phiên bản đơn giản riêng từ đầu. Tôi đang cố gắng tìm ra những gì có thể làm cho nó không hoạt động trong mã phát triển của tôi. Dunno: \
cilphex

@cilphex - Bạn có đang sử dụng phiên bản cập nhật của Angular không?
Joseph Silber

Có - chỉ cần tìm thấy vấn đề. Khi kiểm tra tôi đã thử đặt $event.stopPropagation()ở một nơi không hoạt động. Quên để loại bỏ nó. Vì vậy, ngay cả khi tôi đặt nó ở nơi nó hoạt động, nó đã được gọi là lần thứ hai khi nó không hoạt động. Loại bỏ các bản sao sai lệch và nó thành công. Cảm ơn bạn đã giúp đỡ.
cilphex

101

Tôi thích ý tưởng sử dụng một chỉ thị cho việc này:

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

Sau đó sử dụng chỉ thị như:

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

Nếu bạn muốn, bạn có thể làm cho giải pháp này chung chung hơn như câu trả lời này cho một câu hỏi khác: https://stackoverflow.com/a/14547223/347216


2
Nếu phần tử của bạn được thêm / xóa khỏi DOM một cách linh hoạt, đừng quên xem phần tử của bạn cho sự kiện '$ hủy' để bạn có thể hủy liên kết trình xử lý. Ví dụ: Element.on ('$ kill', function () {/ * thực hiện việc hủy liên kết ở đây * /});
broc.seib

stop-eventmột thuộc tính html? Tôi không thể tìm thấy nó trên Mozilla Developer Network hoặc bất cứ nơi nào khác.
Ehtesh Choudhury

3
@EhteshChoudhury - "stop-event" là chỉ thị mới mà tôi đã tạo trong đoạn mã JS ở trên. Kiểm tra thêm về khai báo và sử dụng các chỉ thị tại đây: docs.angularjs.org/guide/directive
David Ipsen

Giải pháp tốt đẹp! Tôi thực sự đã đăng ký angular-eat-eventchỉ thị trên Bower, hoạt động theo cách tương tự!
gion_13

nó dường như không hoạt động, ng-click đánh giá trước khi chỉ thị. vì vậy tôi có thể ngăn chặn lệnh thực thi nhưng không phải là cách khác.
Rafael

31

Đôi khi, nó có thể có ý nghĩa nhất chỉ để làm điều này:

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

Tôi đã chọn làm theo cách này vì tôi không muốn myClickHandler() dừng việc truyền bá sự kiện ở nhiều nơi khác mà nó được sử dụng.

Chắc chắn, tôi đã có thể thêm một tham số boolean cho hàm xử lý, nhưng stopPropagation()có ý nghĩa hơn nhiều so với chỉ true.


2
Tôi luôn thích phiên bản này, dường như việc lồng các phần tử không liên quan đến chức năng mà tôi đang gọi
mcfedr

Tôi đã phải thêm $event.stopPropagation()vào các yếu tố nội bộ.
Kishor Pawar

@KishorPawar: Tại sao? Có phải cách tôi đã làm nó trong câu trả lời không làm việc cho bạn? Nếu không, sẽ tốt cho chúng tôi khi tìm hiểu xem đó có phải là do sự thay đổi trong cách thức hoạt động của Angular hay do sự cố trong mã của bạn.
Michael Scheper

@MichaelScheper Tôi đã checkboxđược bọc trong divphần tử. Tôi divđang lấy 12 cột và checkboxđang nói 3 cột. Tôi muốn gọi một chức năng Akhi nhấp divvà chức năng Bkhi nhấp vào checkbox. Vì vậy, khi tôi nhấp vào checkboxcả hai chức năng đã được gọi. Khi tôi thêm ng-click="$event.stopPropagation()"vào checkbox, tôi chỉ có chức năng Bđược gọi.
Kishor Pawar

@KishorPawar: À. Vâng, tôi mong đợi điều này, và thực sự, vấn đề stopPropagation()là dừng sự kiện lan truyền đến các yếu tố cha mẹ. Tôi không chắc chắn cách bạn gọi Bvì nó không được chỉ định ng-click, nhưng điểm của câu trả lời là bạn có thể làm điều này : <checkbox ng-click="B(); $event.stopPropagation()">. Bạn không cần phải bao gồm các stopPropagation()chức năng trong B.
Michael Scheper

7

Điều này làm việc cho tôi:

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

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

Tôi muốn xử lý các nhấp chuột có hoặc không nhấn phím Ctrl. Để dừng lựa chọn (và tô sáng) các thành phần HTML trong trình duyệt (IE 11 trong trường hợp của tôi) khi người dùng nhấp vào các mục khác nhau bằng cách giữ phím Ctrl, tôi phải sử dụng sự kiện ng-mousedown và hủy hành vi mặc định thông qua sự kiện $ .preventDefault ()
pholpar

4

Nếu bạn không muốn phải thêm lan truyền dừng cho tất cả các liên kết thì điều này cũng hoạt động. Khả năng mở rộng hơn một chút.

$scope.hideOverlay( $event ){

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

}

2
Giải pháp này hoạt động với ví dụ được cung cấp (rất tuyệt!), Tuy nhiên, nó không hoạt động khi div bên ngoài có n hoặc nhiều phần tử lồng nhau trước khi nhấp vào href / ng-click. Sau đó, khi cuộc gọi lại hideOverlay () được gọi, mục tiêu là một trong các phần tử lồng nhau và không phải là phần tử ngoài cùng với một lệnh ng-click. Để xử lý các phần tử lồng nhau có thể hiểu được, người ta có thể sử dụng jquery để lấy cha mẹ và xem liệu cha mẹ có thể nhấp đầu tiên (a, ng-click, v.v.) có lớp phủ không, nhưng có vẻ hơi cồng kềnh ...
Amir

console.log ("potatooverlay" .indexOf ("lớp phủ")! = -1); console.log (/ \ boverlay \ b / g.test ("potatooverlay"));

angular sẽ cho phép bạn làm event.target.classList.indexOf('overlay') Và thủ thuật đó hoạt động tốt ngay cả khi div được lồng trong các div khác
deltree

0

Nếu bạn chèn ng-click = "$ event.stopPropagation" vào phần tử cha của mẫu, thì stopPropogation sẽ bị bắt khi nó nổi lên trên cây, do đó bạn chỉ phải viết một lần cho toàn bộ mẫu.


0

Bạn có thể đăng ký một chỉ thị khác ở trên ng-clickđó sửa đổi hành vi mặc định ng-clickvà dừng việc truyền bá sự kiện. Bằng cách này, bạn sẽ không phải thêm $event.stopPropagationbằng tay.

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>

Bộ điều khiển IN sử dụng

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

Gửi sự kiện đầu tiên theo phương thức để thực hiện
Dinesh Jain

Câu trả lời của bạn không thêm gì vào câu hỏi được chấp nhận 4 năm trước
Ilya Luzyanin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.