เป็นวิธีที่ดีที่สุดในการยกเลิกการเผยแพร่กิจกรรมระหว่างการโทร ng-click ซ้อนอยู่?


180

นี่คือตัวอย่าง สมมติว่าฉันต้องการให้มีภาพซ้อนทับเหมือนเว็บไซต์จำนวนมาก ดังนั้นเมื่อคุณคลิกภาพขนาดย่อจะมีการซ้อนทับสีดำปรากฏขึ้นเหนือหน้าต่างทั้งหมดของคุณและภาพที่มีขนาดใหญ่กว่าจะอยู่กึ่งกลาง การคลิกการซ้อนทับสีดำจะเป็นการยกเลิก การคลิกที่ภาพจะเรียกใช้ฟังก์ชันที่แสดงภาพถัดไป

html ที่:

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

จาวาสคริปต์:

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ในตอนท้ายของการทำงาน
Jonathan de M.

1
ผมเชื่อว่าเป็นวิธีที่จะทำมันให้ไม่ได้onclick ng-click
Michael Scheper

คำตอบ:


193

สิ่งที่ @JosephSilber พูดหรือส่ง $ event object ไปที่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 ไม่ทำงานอีกต่อไป แต่ PreventDefault () ทำงานได้ ข้อแตกต่างเพียงอย่างเดียวคือฉันโทรไปที่ 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

ใช่มันใช้งานได้ ... มันยังทำงานได้เมื่อฉันเขียนเวอร์ชันง่าย ๆ แยกจากศูนย์ ฉันพยายามหาว่าอะไรจะทำให้รหัสพัฒนาของฉันไม่ทำงาน Dunno: \
cilphex

@cilphex - คุณใช้ Angular รุ่นล่าสุดหรือไม่
Joseph Silber

ใช่ - เพิ่งพบปัญหา เมื่อทดสอบฉันลองวาง$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 () {/ * ทำการ unbinding ที่นี่ * /});
broc.seib

เป็นstop-eventแอตทริบิวต์ HTML ได้ไหม? ฉันหามันไม่พบบน Mozilla Developer Network หรือที่อื่น ๆ
Ehtesh Choudhury

3
@EhteshChoudhury - "stop-event" เป็นคำสั่งใหม่ที่ฉันสร้างขึ้นในข้อมูลโค้ด JS ด้านบน ตรวจสอบข้อมูลเพิ่มเติมเกี่ยวกับการประกาศและการใช้คำสั่งที่นี่: docs.angularjs.org/guide/directive
เดวิด Ipsen

ทางออกที่ดี! จริง ๆ แล้วฉันลงทะเบียนangular-eat-eventคำสั่งในร่มซึ่งทำงานในลักษณะที่คล้ายกัน!
gion_13

ดูเหมือนจะใช้งานไม่ได้ ng-click จะประเมินผลก่อนคำสั่ง ดังนั้นฉันสามารถป้องกันคำสั่งจากการดำเนินการ แต่ไม่ใช่วิธีอื่น ๆ
Rafael

31

บางครั้งมันอาจเหมาะสมที่สุดที่จะทำสิ่งนี้:

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

ฉันเลือกที่จะทำแบบนี้เพราะฉันไม่ต้องการ myClickHandler()หยุดการเผยแพร่กิจกรรมในสถานที่อื่น ๆ ที่ใช้

แน่นอนว่าฉันจะได้เพิ่มพารามิเตอร์บูลฟังก์ชั่นการจัดการ แต่เป็นความหมายมากขึ้นกว่าเพียงแค่stopPropagation()true


2
ฉันชอบเวอร์ชั่นนี้เสมอดูเหมือนว่าการซ้อนกันขององค์ประกอบไม่ควรเกี่ยวข้องกับฟังก์ชั่นที่ฉันกำลังเรียก
mcfedr

ฉันต้องเพิ่ม$event.stopPropagation()องค์ประกอบภายใน
Kishor Pawar

@KishorPawar: เพราะอะไร วิธีที่ฉันทำในคำตอบนั้นไม่ได้ผลสำหรับคุณหรือไม่? หากไม่เป็นเช่นนั้นจะเป็นการดีสำหรับเราที่จะทราบว่าเป็นเพราะการเปลี่ยนแปลงของวิธีการทำงานของแองกูลาร์หรือปัญหาในรหัสของคุณ
Michael Scheper

@MichaelScheper ฉันมีองค์ประกอบcheckboxในห่อdivแล้ว ของฉันdivกำลัง 12 คอลัมน์และcheckboxกำลังพูด 3 คอลัมน์ ผมอยากจะเรียกฟังก์ชั่นAในการคลิกdivและฟังก์ชั่นในการคลิกB checkboxดังนั้นเมื่อฉันคลิกcheckboxทั้งสองฟังก์ชั่นก็ถูกเรียกใช้ เมื่อฉันเพิ่มng-click="$event.stopPropagation()"ให้กับcheckboxผมได้เพียงฟังก์ชั่นBเรียก
Kishor Pawar

@KishorPawar: อ่า ใช่ฉันคาดหวังสิ่งนี้และที่จริงแล้วประเด็นstopPropagation()คือเพื่อหยุดเหตุการณ์ที่แพร่กระจายไปยังองค์ประกอบหลัก ผมไม่แน่ใจว่าวิธีการที่คุณกำลังเรียกBเพราะมันไม่ได้ specififed ในแต่จุดของคำตอบคือการที่คุณสามารถทำเช่นนี้:ng-click <checkbox ng-click="B(); $event.stopPropagation()">คุณไม่จำเป็นต้องรวมฟังก์ชั่นในstopPropagation() B
Michael Scheper

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 ด้านนอกมีองค์ประกอบซ้อนกันมากกว่าหนึ่งรายการก่อน href / ng-click จากนั้นเมื่อเรียกใช้การเรียกคืน hideOverlay () เป้าหมายนั้นเป็นหนึ่งในองค์ประกอบที่ซ้อนกันและไม่ใช่องค์ประกอบด้านนอกสุดที่มีคำสั่ง ng-click เพื่อจัดการกับองค์ประกอบที่ซ้อนกันกลัวใครสามารถใช้ jQuery ที่จะได้รับพ่อแม่และดูว่าสามารถคลิกได้ครั้งแรก (มี ng คลิก ฯลฯ ) ผู้ปกครองมีระดับซ้อนทับ แต่ที่มีลักษณะที่ยุ่งยากนิด ...
อาเมียร์

console.log ("potatooverlay" .indexOf ("overlay")! = -1); console.log (/ \ boverlay \ b / g.test ( "potatooverlay"));

เชิงมุมจะช่วยให้คุณทำevent.target.classList.indexOf('overlay') และเคล็ดลับที่ทำงานได้ดีแม้ในขณะที่ div ซ้อนกันใน divs อื่น ๆ
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>

ใช้ตัวควบคุม

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

ก่อนส่งกิจกรรมในวิธีการทำ
Dinesh Jain

คำตอบของคุณไม่ได้เพิ่มสิ่งใดเลยที่ยอมรับเมื่อ 4 ปีที่แล้ว
Ilya Luzyanin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.