ไม่สามารถเข้าใจพารามิเตอร์ useCapture ใน addEventListener


290

ฉันได้อ่านบทความที่https://developer.mozilla.org/en/DOM/element.addEventListenerแต่ไม่เข้าใจuseCaptureคุณลักษณะ คำจำกัดความคือ:

หากเป็นจริง useCapture จะระบุว่าผู้ใช้ต้องการเริ่มต้นการจับภาพ หลังจากเริ่มต้นการจับภาพเหตุการณ์ทั้งหมดของประเภทที่ระบุจะถูกส่งไปยังผู้ฟังที่ลงทะเบียนก่อนที่จะถูกส่งไปยัง EventTargets ใด ๆ ที่อยู่ด้านล่างในต้นไม้ DOM เหตุการณ์ที่เดือดพล่ามผ่านต้นไม้จะไม่ทำให้ผู้ฟังที่ได้รับมอบหมายให้ใช้การดักจับ

ในเหตุการณ์รหัสแม่นี้ก่อให้เกิดก่อนที่เด็กดังนั้นฉันไม่สามารถเข้าใจพฤติกรรมของวัตถุวัตถุเอกสารมี usecapture จริงและลูก div มีชุด usecapture เท็จและเอกสาร usecapture เป็นไปดังนั้นคุณสมบัติของเอกสารที่ต้องการมากกว่าเด็ก

function load() {
  document.addEventListener("click", function() {
    alert("parent event");
  }, true);

  document.getElementById("div1").addEventListener("click", function() {
    alert("child event");
  }, false);
}
<body onload="load()">
  <div id="div1">click me</div>
</body>

คำตอบ:


350

เหตุการณ์สามารถเปิดใช้งานได้สองครั้ง: ที่จุดเริ่มต้น ("การจับภาพ") และที่จุดสิ้นสุด ("ฟองสบู่") เหตุการณ์จะถูกดำเนินการตามลำดับที่กำหนดไว้ สมมติว่าคุณกำหนดผู้ฟังเหตุการณ์ 4 คน:

window.addEventListener("click", function(){console.log(1)}, false);
window.addEventListener("click", function(){console.log(2)}, true);
window.addEventListener("click", function(){console.log(3)}, false);
window.addEventListener("click", function(){console.log(4)}, true);

ข้อความบันทึกจะปรากฏขึ้นตามลำดับนี้:

  • 2(นิยามไว้ก่อนโดยใช้capture=true)
  • 4(กำหนดครั้งที่สองโดยใช้capture=true)
  • 1(เหตุการณ์แรกที่กำหนดด้วยcapture=false)
  • 3(เหตุการณ์ที่สองที่กำหนดไว้ด้วยcapture=false)

49
ไม่รับประกันคำสั่งในการดำเนินการ : no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget. ฉันไม่ได้ทดสอบเบราว์เซอร์ทั้งหมดดังนั้นพวกเขาทั้งหมดอาจเกิดขึ้นเพื่อใช้งานในลักษณะเดียวกัน อย่างไรก็ตามการจับภาพเหตุการณ์จะทำก่อนที่จะไม่จับภาพเหตุการณ์
beatgammit

47
@tjameson ลำดับของการดำเนินการมีการรับประกันในตัวตายตัวแทนของข้อมูลจำเพาะ DOM2, เหตุการณ์ DOM3 : "การดำเนินการจะต้องพิจารณาผู้ฟังเหตุการณ์ผู้สมัครรับเลือกตั้งของเป้าหมายปัจจุบันนี้จะต้องเป็นรายการของผู้ฟังเหตุการณ์ทั้งหมดที่ลงทะเบียนไว้กับเป้าหมายปัจจุบันใน คำสั่งของการลงทะเบียน "
Rob W

1
ดังนั้นนี้เป็นพื้นมีการทำตามคำสั่งฉันเดาเหตุการณ์
slier

1
@ ก่อนหน้านี้ใช่ลำดับที่ตัวจัดการหลายตัวสำหรับเหตุการณ์เดียวกันจะถูกดำเนินการ
JMD

6
ไม่รู้เลยว่าทำไมนี่คือคำตอบที่ได้รับการยอมรับตั้งแต่ afaik การจับภาพและการพูดพล่ามเกี่ยวกับพฤติกรรมการแพร่กระจายและไม่เกี่ยวกับการกำหนดลำดับการดำเนินการสำหรับตัวจัดการเหตุการณ์หลายตัวที่อยู่ติดกัน
georaldc

272

ฉันพบว่าแผนภาพนี้มีประโยชน์มากสำหรับการทำความเข้าใจขั้นตอนการจับ / เป้าหมาย / ฟอง: http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

ด้านล่างเนื้อหาที่ดึงมาจากลิงก์

ขั้นตอน

เหตุการณ์ถูกส่งไปตามเส้นทางจากรูทของทรีไปยังโหนดเป้าหมายนี้ จากนั้นสามารถจัดการแบบโลคัลที่ระดับโหนดเป้าหมายหรือจากบรรพบุรุษของเป้าหมายใด ๆ ที่สูงกว่าในทรี การจัดส่งเหตุการณ์ (หรือเรียกว่าการเผยแพร่เหตุการณ์) เกิดขึ้นในสามเฟสและลำดับต่อไปนี้:

  1. เฟสการดักจับ: เหตุการณ์ถูกส่งไปยังบรรพบุรุษของเป้าหมายจากรูทของทรีไปยังพาเรนต์โดยตรงของโหนดเป้าหมาย
  2. เฟสเป้าหมาย: เหตุการณ์ถูกส่งไปยังโหนดเป้าหมาย
  3. เฟสเดือดปุด ๆ : เหตุการณ์ถูกส่งไปยังบรรพบุรุษของเป้าหมายจากผู้ปกครองโดยตรงของโหนดเป้าหมายไปยังรากของต้นไม้

การแสดงกราฟิกของเหตุการณ์ที่ส่งในทรี DOM โดยใช้โฟลว์เหตุการณ์ DOM

บรรพบุรุษของเป้าหมายได้รับการพิจารณาก่อนที่จะส่งเหตุการณ์ครั้งแรก หากโหนดเป้าหมายถูกลบในระหว่างการแจกจ่ายหรือบรรพบุรุษของเป้าหมายถูกเพิ่มหรือลบการเผยแพร่เหตุการณ์จะขึ้นอยู่กับโหนดเป้าหมายและบรรพบุรุษของเป้าหมายที่กำหนดก่อนการจัดส่ง

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


1
แผนภาพที่ดีมาก!
อเล็กซ์

1
วิธีการเกี่ยวกับเด็ก ๆ ของโหนดเป้าหมาย? พวกเขาจะได้รับเหตุการณ์เมื่อใด
Aurimas

จริง ๆ แล้วเป็นรากของต้นไม้Windowแทนที่จะเป็นdocumentเพราะdocumentเป็นลูกของWindow?
stackjlei

1
@Aurimas พวกเขาไม่ได้มันไม่สมเหตุสมผล เป้าหมายคือองค์ประกอบภายในที่ควรได้รับเหตุการณ์ หากคุณคลิกองค์ประกอบ <body> (ตำแหน่งว่าง) องค์ประกอบทั้งหมดใน <body> (= องค์ประกอบทั้งหมดของหน้า) ไม่ควรได้รับเหตุการณ์คลิกอย่างชัดเจน
amik

1
ฉันแค่หวังว่าทรัพยากรทั้งหมดที่อธิบาย "อะไร" รวมถึง "ทำไม" ปิดเพื่อ googling เพิ่มเติมตามปกติ
aaaaaa

80

จับภาพเหตุการณ์ ( useCapture = true) กับเหตุการณ์ฟอง (useCapture = false )

อ้างอิง MDN

  • กิจกรรมจับภาพจะถูกจัดส่งก่อนกิจกรรมฟอง
  • ลำดับการเผยแพร่กิจกรรมคือ
    1. ผู้ปกครองจับภาพ
    2. เด็กจับภาพ
    3. การดักจับเป้าหมายและบับเบิลเป้าหมาย
      • ตามลำดับพวกเขาลงทะเบียน
      • เมื่อองค์ประกอบเป็นเป้าหมายของเหตุการณ์useCaptureพารามิเตอร์ไม่สำคัญ (ขอบคุณ @bam และ @ legend80s)
    4. Bubble เด็ก
    5. ฟองผู้ปกครอง
  • stopPropagation() จะหยุดการไหล

ใช้การไหลของการจับภาพ

การสาธิต

ผลลัพธ์:

  1. ผู้ปกครองจับภาพ
  2. Target Bubble 1

    (เนื่องจากการดักจับและบับเบิลของเป้าหมายจะเรียกตามลำดับที่ลงทะเบียนดังนั้นเหตุการณ์บับเบิ้ลจึงถูกเรียกใช้ก่อนการจับภาพเหตุการณ์)

  3. การจับเป้าหมาย

  4. Target Bubble 2
  5. ฟองผู้ปกครอง

var parent = document.getElementById('parent'),
target = document.getElementById('target');

target.addEventListener('click', function (e) { 
console.log('Target Bubble 1');
// e.stopPropagation();
}, false);

target.addEventListener('click', function (e) { 
console.log('Target Capture');
// e.stopPropagation();
}, true);

target.addEventListener('click', function (e) { 
console.log('Target Bubble 2');
// e.stopPropagation();
}, false);

parent.addEventListener('click', function (e) { 
console.log('Parent Capture');
// e.stopPropagation();
}, true);

parent.addEventListener('click', function (e) { 
console.log('Parent Bubble');
// e.stopPropagation();
}, false);
<div id="parent">
    <button id="target" style="padding: 1em 0.8em;">
        Trigger event
    </button>
</div>


1
มีข้อผิดพลาดในตัวอย่าง: คุณประกาศเหตุการณ์เด็กตามลำดับ: 1. การจับเด็ก 2. ฟองเด็กมันสำคัญ! เพียงเพราะถ้าเด็กจะเป็นเป้าหมายของเหตุการณ์ผู้ฟังจะถูกเรียกตามลำดับเดียวกัน ดูหมายเหตุที่ MDN: เมื่อองค์ประกอบคือเป้าหมายของพารามิเตอร์ 'useCapture' เหตุการณ์นั้นไม่สำคัญ ( developer.mozilla.org/en-US/docs/Web/API/EventTarget/
......

1
หมายเหตุ : สำหรับผู้ฟังเหตุการณ์ที่แนบกับเป้าหมายเหตุการณ์เหตุการณ์นั้นอยู่ในขั้นตอนเป้าหมายแทนที่จะเป็นเฟสการดักจับและเฟส Events in the target phase will trigger all listeners on an element in the order they were registered, regardless of the useCapture parameter.จากdeveloper.mozilla.org/en-US/docs/Web/API/EventTarget/… . ดังนั้นจึงไม่มีเฟสของ "Children Capture" และ "Children Bubble"
legend80s

และนั่นอธิบายว่าเหตุใดการรันตัวอย่างจึงสร้าง "Children bubble 1" ก่อน "Children capture" เมื่อไดอะแกรมแสดงให้เห็นว่า "การจับภาพ" ควรเกิดขึ้นก่อนเสมอสำหรับองค์ประกอบใด ๆ !
Gershom

18

เมื่อคุณพูดว่า useCapture = true เหตุการณ์จะดำเนินการจากบนลงล่างในช่วงการดักจับเมื่อมีการเท็จจะทำให้เกิดฟองจากล่างขึ้นบน


11

มันคือทั้งหมดที่เกี่ยวกับโมเดลเหตุการณ์: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow คุณสามารถจับเหตุการณ์ในเฟสที่เดือดร้อนหรือในระยะการจับ ทางเลือกของคุณ.
ลองดูที่http://www.quirksmode.org/js/events_order.html - คุณจะพบว่ามันมีประโยชน์มาก


1
ลิงค์ไปยัง w3 นั้นเป็นหรือแม้แต่มีประโยชน์น้อยกว่าการค้นหาของ google ฉันไม่สามารถเข้าใจอะไรที่นั่น
มูฮัมหมัดอูเมอ

3
ใช่ลิงค์ w3 เป็นเพียงคำจำนวนมาก แต่ตรงข้ามกับลิงค์ที่สองไปยังไซต์quirksmodeอธิบายหัวข้อได้ดีมากและสั้น ๆ
Stano

11

ตัวอย่างรหัส:

<div id="div1" style="background:#9595FF">
  Outer Div<br />
  <div id="div2" style="background:#FFFFFF">
    Inner Div
  </div>
</div>

รหัส Javascript:

d1 = document.getElementById("div1");
d2 = document.getElementById("div2");

หากทั้งคู่ถูกตั้งค่าเป็นเท็จ

d1.addEventListener('click',function(){alert("Div 1")},false);
d2.addEventListener('click',function(){alert("Div 2")},false);

ดำเนินการ: Onclicking Inner Div การแจ้งเตือนจะแสดงเป็น: Div 2> Div 1

สคริปต์จะถูกเรียกใช้จากองค์ประกอบภายใน: Event Bubbling (useCapture ถูกตั้งค่าเป็นเท็จ)

div 1 ถูกตั้งค่าเป็น true และ div 2 ตั้งค่าเป็น false

d1.addEventListener('click',function(){alert("Div 1")},true);
d2.addEventListener('click',function(){alert("Div 2")},false);

ดำเนินการ: Onclicking Inner Div การแจ้งเตือนจะแสดงเป็น: Div 1> Div 2

สคริปต์จะถูกเรียกใช้งานจากองค์ประกอบส่วนต้น / ส่วนนอก: การจับภาพเหตุการณ์ (useCapture ถูกตั้งค่าเป็นจริง)

div 1 ถูกตั้งค่าเป็นเท็จและ div 2 ตั้งค่าเป็นจริง

d1.addEventListener('click',function(){alert("Div 1")},false);
d2.addEventListener('click',function(){alert("Div 2")},true);

ดำเนินการ: Onclicking Inner Div การแจ้งเตือนจะแสดงเป็น: Div 2> Div 1

สคริปต์จะถูกเรียกใช้จากองค์ประกอบภายใน: Event Bubbling (useCapture ถูกตั้งค่าเป็นเท็จ)

div 1 ถูกตั้งค่าเป็นจริงและ div 2 ตั้งค่าเป็นจริง

d1.addEventListener('click',function(){alert("Div 1")},true);
d2.addEventListener('click',function(){alert("Div 2")},true);

ดำเนินการ: Onclicking Inner Div การแจ้งเตือนจะแสดงเป็น: Div 1> Div 2

ที่นี่สคริปต์จะถูกดำเนินการจากองค์ประกอบบรรพบุรุษ / องค์ประกอบภายนอก: การจับภาพกิจกรรมเนื่องจาก useCapture ได้รับการตั้งค่าเป็นจริง


1
ความหมายของเครื่องหมายบั้ง "มากกว่า" ในบริบทนี้คืออะไร
2540625

9

สรุป:

DOMข้อมูลจำเพาะที่อธิบายไว้ใน:

https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

ทำงานในลักษณะต่อไปนี้:

เหตุการณ์ที่ถูกส่งไปตามเส้นทางจากราก ( document) ของต้นไม้ไปยังโหนดเป้าหมาย โหนดเป้าหมายเป็นHTMLองค์ประกอบที่ลึกที่สุดคือ event.target การจัดส่งเหตุการณ์ (หรือเรียกว่าการเผยแพร่เหตุการณ์) เกิดขึ้นในสามเฟสและลำดับต่อไปนี้:

  1. ขั้นตอนการดักจับ:เหตุการณ์ถูกส่งไปยังบรรพบุรุษของเป้าหมายจากรูทของทรี ( document) ไปยังพาเรนต์โดยตรงของโหนดเป้าหมาย
  2. เฟสเป้าหมาย:เหตุการณ์ถูกส่งไปยังโหนดเป้าหมาย เฟสเป้าหมายมักจะเป็นhtmlองค์ประกอบที่ลึกที่สุดที่เหตุการณ์ถูกกำจัดออกไป
  3. เฟสเดือดปุด ๆ :เหตุการณ์ถูกส่งไปยังบรรพบุรุษของเป้าหมายจากผู้ปกครองโดยตรงของโหนดเป้าหมายไปยังรากของต้นไม้

เหตุการณ์เดือดปุด ๆ จับเหตุการณ์เป้าหมายเหตุการณ์

ตัวอย่าง:

// bubbling handlers, third argument (useCapture) false (default)
document.getElementById('outerBubble').addEventListener('click', () => {
  console.log('outerBubble');
}, false)

document.getElementById('innerBubble').addEventListener('click', () => {
  console.log('innerBubble');
}, false)


// capturing handlers, third argument (useCapture)  true
document.getElementById('outerCapture').addEventListener('click', () => {
  console.log('outerCapture');
}, true)

document.getElementById('innerCapture').addEventListener('click', () => {
  console.log('innerCapture');
}, true)
div:hover{
  color: red;
  cursor: pointer;
}
<!-- event bubbling -->
<div id="outerBubble">
  <div id="innerBubble">click me to see Bubbling</div>
</div>


<!-- event capturing -->
<div id="outerCapture">
  <div id="innerCapture">click me to see Capturing</div>
</div>

ตัวอย่างข้างต้นแสดงให้เห็นถึงความแตกต่างระหว่างการทำ bubbling กับการจับกิจกรรม เมื่อเพิ่มผู้ฟังเหตุการณ์ด้วยaddEventListenerจะมีองค์ประกอบที่สามที่เรียกว่า useCapture สิ่งนี้booleanเมื่อตั้งค่าtrueให้อนุญาตให้ผู้ฟังเหตุการณ์ใช้การดักจับเหตุการณ์แทนการทำให้เกิดเหตุการณ์

ในตัวอย่างของเราเมื่อเราตั้งอาร์กิวเมนต์ useCapture ให้falseเราเห็นว่ามีเหตุการณ์เดือดปุด ๆ เหตุการณ์แรกในเฟสเป้าหมายนั้นเริ่มทำงาน (บันทึก InnerBubble) จากนั้นผ่านเหตุการณ์ที่ทำให้เดือดดาลเหตุการณ์ในองค์ประกอบหลักจะเริ่มทำงาน (บันทึก OuterBubble)

เมื่อเราตั้งค่าอาร์กิวเมนต์ useCapture ให้trueเราเห็นว่าเหตุการณ์ในด้านนอก<div>ถูกไล่ออกก่อน นี่เป็นเพราะตอนนี้เหตุการณ์เริ่มทำงานในระยะการจับภาพไม่ใช่ระยะการเดือด


7

ให้สามขั้นตอนของการเดินทางเหตุการณ์:

  1. ขั้นตอนการจับภาพ : เหตุการณ์ที่ส่งไปบรรพบุรุษของเป้าหมายจากรากของต้นไม้ไปยังผู้ปกครองโดยตรงของโหนดเป้าหมาย
  2. เฟสเป้าหมาย : เหตุการณ์ที่ถูกส่งไปยังโหนดเป้าหมาย
  3. เฟสเดือด : เหตุการณ์ที่ส่งไปบรรพบุรุษของเป้าหมายจากผู้ปกครองโดยตรงของโหนดเป้าหมายไปยังรากของต้นไม้

useCaptureบ่งชี้ว่าขั้นตอนใดที่การเดินทางของเหตุการณ์จะเปิดอยู่:

ถ้าtrue, useCaptureแสดงให้เห็นว่าผู้ใช้มีความประสงค์ที่จะเพิ่มฟังเหตุการณ์สำหรับขั้นตอนการจับภาพเพียงอย่างเดียวคือฟังเหตุการณ์นี้จะไม่ถูกเรียกในช่วงเป้าหมายและขั้นตอนเดือดปุด ๆ หากfalseผู้ฟังเหตุการณ์จะถูกกระตุ้นระหว่างเป้าหมายและเฟสเดือดดาล

แหล่งที่มานั้นเหมือนกับคำตอบที่ดีที่สุดอันดับสอง: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases


2

คำสั่งของคำจำกัดความจะมีความสำคัญหากรายการอยู่ในระดับเดียวกัน หากคุณกลับลำดับของคำจำกัดความในรหัสของคุณคุณจะได้ผลลัพธ์เดียวกัน

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

หากคุณตั้งค่า useCapture เป็น true สำหรับตัวจัดการเหตุการณ์ทั้งสอง - ไม่ว่าจะเรียงตามคำจำกัดความอย่างไรตัวจัดการเหตุการณ์หลักจะถูกเรียกใช้ก่อนเพราะมาก่อนลูกในเฟสการดักจับ

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

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