เหตุการณ์เดือดดาลและการจับคืออะไร?


1041

อะไรคือความแตกต่างระหว่างเหตุการณ์เดือดและการจับ? เมื่อใดที่หนึ่งควรใช้ bubbling vs capture


2
ฉันแนะนำลิงค์ที่มีประโยชน์นี้: javascript.info/bubbling-and-capturing
— ชุมชน Ans

คำตอบ:


1439

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

ด้วย bubbling เหตุการณ์จะถูกจับและจัดการโดยองค์ประกอบด้านในสุดก่อนแล้วจึงแพร่กระจายไปยังองค์ประกอบด้านนอก

เมื่อจับภาพเหตุการณ์จะถูกจับครั้งแรกโดยองค์ประกอบนอกสุดและแพร่กระจายไปยังองค์ประกอบภายใน

การจับภาพนั้นเรียกอีกอย่างว่า "trickling" ซึ่งช่วยในการจำลำดับการแพร่กระจาย:

หยดลงมา

ย้อนกลับไปในสมัยก่อน Netscape สนับสนุนการจับภาพเหตุการณ์ขณะที่ Microsoft เลื่อนตำแหน่งการจัดกิจกรรม ทั้งสองเป็นส่วนหนึ่งของมาตรฐานเหตุการณ์วัตถุจำลองเอกสาร W3C (2000)

IE <9 ใช้เฉพาะเหตุการณ์เดือดขณะที่ IE9 + และเบราว์เซอร์หลักทั้งหมดรองรับทั้งสองอย่าง ในทางกลับกันประสิทธิภาพของการขัดถูเหตุการณ์อาจลดลงเล็กน้อยสำหรับ DOM ที่ซับซ้อน

เราสามารถใช้addEventListener(type, listener, useCapture)เพื่อลงทะเบียนตัวจัดการเหตุการณ์สำหรับใน bubbling (ค่าเริ่มต้น) หรือโหมดการจับภาพ trueการใช้รูปแบบการจับภาพผ่านอาร์กิวเมนต์ที่สามเป็น

ตัวอย่าง

<div>
    <ul>
        <li></li>
    </ul>
</div>

ในโครงสร้างด้านบนสมมติว่ามีเหตุการณ์การคลิกเกิดขึ้นในliองค์ประกอบ

ในการจับภาพรูปแบบการจัดกิจกรรมจะได้รับการจัดการโดยdivครั้งแรก (คลิกจัดการเหตุการณ์ในdivจะยิงครั้งแรก) จากนั้นในแล้วที่สุดท้ายในองค์ประกอบเป้าหมายulli

ในรูปแบบ bubbling สิ่งที่ตรงกันข้ามจะเกิดขึ้น: เหตุการณ์จะได้รับการจัดการเป็นครั้งแรกจากliนั้นตามด้วยulและในที่สุดจะเป็นdivองค์ประกอบ

สำหรับข้อมูลเพิ่มเติมโปรดดู

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

var logElement = document.getElementById('log');

function log(msg) {
    logElement.innerHTML += ('<p>' + msg + '</p>');
}

function capture() {
    log('capture: ' + this.firstChild.nodeValue.trim());
}

function bubble() {
    log('bubble: ' + this.firstChild.nodeValue.trim());
}

function clearOutput() {
    logElement.innerHTML = "";
}

var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
    divs[i].addEventListener('click', capture, true);
    divs[i].addEventListener('click', bubble, false);
}
var clearButton = document.getElementById('clear');
clearButton.addEventListener('click', clearOutput);
p {
    line-height: 0;
}

div {
    display:inline-block;
    padding: 5px;

    background: #fff;
    border: 1px solid #aaa;
    cursor: pointer;
}

div:hover {
    border: 1px solid #faa;
    background: #fdd;
}
<div>1
    <div>2
        <div>3
            <div>4
                <div>5</div>
            </div>
        </div>
    </div>
</div>
<button id="clear">clear output</button>
<section id="log"></section>

ตัวอย่างที่ JSFiddle


41
useCaptureรองรับตอนนี้ใน IE> = 9. source
— beatgammit

7
ฉันรู้ว่ามันสายเกินไปที่จะใส่ความคิดเห็น แต่บทความดีดีที่ฉันพบที่นี่catcode.com/domcontent/events/capture.html
— แค่โค้ด

3
เป็นtriclklingเช่นเดียวกับcapturing? พูดคุยเกี่ยวกับครอกTrickling v. Bubblingในการพูดคุยวิดีโอนี้ - youtube.com/watch?v=Fv9qT9joc0M&list=PL7664379246A246CB1 hr 5 minutesรอบ
— เควินเมเรดิ ธ

1
@KevinMeredith สิ่งเดียวกัน "เติมได้ Tricking" เพียงแค่ทำให้มันง่ายต่อการจำสิ่งที่ทั้งสองรุ่นไม่ (หยดลงฟองขึ้น )
— แมว

7
คำตอบข้างต้นถูกต้องเกี่ยวกับการสั่งซื้อในคำอธิบายโดยละเอียด แต่คุณคิดว่าหยดที่เกิดขึ้นที่สองด้วย "ฟองขึ้นหยดลง" กิจกรรมจะต้องผ่านระยะการจับภาพก่อนระยะฟอง คำสั่งที่ถูกต้องคือtrickle down=> onElement=>bubble up
— runspired

513

รายละเอียด:

quirksmode.orgมีคำอธิบายที่ดีเกี่ยวกับเรื่องนี้ สรุป (คัดลอกมาจาก quirksmode):

การจับภาพเหตุการณ์

เมื่อคุณใช้การจับภาพเหตุการณ์

               | |
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  \ /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------

ตัวจัดการเหตุการณ์ของ element1 ยิงก่อนตัวจัดการเหตุการณ์ขององค์ประกอบ 2 จะยิงครั้งสุดท้าย

เหตุการณ์เดือดปุด ๆ

เมื่อคุณใช้เดือดปุด ๆ เหตุการณ์

               / \
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------

ตัวจัดการเหตุการณ์ขององค์ประกอบ 2 ยิงก่อนตัวจัดการเหตุการณ์ขององค์ประกอบ 1 จะยิงครั้งสุดท้าย


จะใช้อะไรดี?

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


ไม่เกิดขึ้นทั้งการจับครั้งแรกแล้วเดือดปุด ๆ เหตุการณ์การจัดส่งคืออะไร?
— Suraj Jain

ตัวอย่างกราฟิกอยู่ที่นี่: javascript.info/bubbling-and-capturing
— ชุมชน Ans

71

หากมีสององค์ประกอบองค์ประกอบที่ 1 และองค์ประกอบที่ 2 องค์ประกอบที่ 2 อยู่ในองค์ประกอบที่ 1 และเราแนบตัวจัดการเหตุการณ์ที่มีทั้งสององค์ประกอบให้พูด onClick ตอนนี้เมื่อเราคลิกที่องค์ประกอบ 2 แล้ว eventHandler สำหรับองค์ประกอบทั้งสองจะถูกดำเนินการ ตอนนี้คำถามคือลำดับเหตุการณ์ที่จะดำเนินการ หากเหตุการณ์ที่แนบมากับองค์ประกอบ 1 ดำเนินการครั้งแรกจะเรียกว่าการจับภาพเหตุการณ์และหากเหตุการณ์ที่แนบมากับองค์ประกอบ 2 ดำเนินการครั้งแรกนี้เรียกว่าเหตุการณ์เดือดปุด ๆ ตาม W3C เหตุการณ์จะเริ่มขึ้นในช่วงการจับภาพจนกว่าจะถึงเป้าหมายกลับมาที่องค์ประกอบจากนั้นจะเริ่มเดือด

สถานะการดักจับและการเดือดกำลังเป็นที่รู้จักโดยพารามิเตอร์ useCapture ของเมธอด addEventListener

eventTarget.addEventListener (ชนิด, ฟัง [useCapture]);

โดยค่าเริ่มต้น useCapture เป็นเท็จ มันหมายความว่ามันอยู่ในช่วงเดือดปุด ๆ

var div1 = document.querySelector("#div1");
var div2 = document.querySelector("#div2");

div1.addEventListener("click", function (event) {
  alert("you clicked on div 1");
}, true);

div2.addEventListener("click", function (event) {
  alert("you clicked on div 2");
}, false);
#div1{
  background-color:red;
  padding: 24px;
}

#div2{
  background-color:green;
}
<div id="div1">
  div 1
  <div id="div2">
    div 2
  </div>
</div>

โปรดลองเปลี่ยนจริงและเท็จ


2
@masterxilo: ความจำเป็นสำหรับซอไม่มี StackOverflow ขณะนี้สนับสนุนรหัสแบบอินไลน์ (ตัวอย่างสแต็ค)
— Dan Dascalescu

the event will start in the capturing phase untill it reaches the target comes back to the element and then it starts bubblingเกี่ยวกับ ฉันพบเพียง addEventListenerมีพารามิเตอร์useCaptureที่สามารถตั้งค่าเป็นจริงหรือเท็จ และใน HTML 4.0, ฟังเหตุการณ์ที่ถูกระบุว่าเป็นลักษณะขององค์ประกอบuseCapture defaults to falseและ คุณสามารถลิงค์ไปยังข้อมูลจำเพาะที่ยืนยันสิ่งที่คุณเขียนได้หรือไม่?
— surfmuggle

25

ฉันได้พบบทช่วยสอนนี้ที่ javascript.infoอย่างชัดเจนในการอธิบายหัวข้อนี้ และการสรุป 3 คะแนนในตอนท้ายพูดถึงประเด็นสำคัญอย่างยิ่ง ฉันพูดที่นี่:

  1. เหตุการณ์แรกจะถูกจับลงไปที่เป้าหมายที่ลึกที่สุดจากนั้นทำให้เกิดฟองขึ้น ใน IE <9 พวกเขามี แต่ฟองสบู่เท่านั้น
  2. ตัวจัดการทั้งหมดทำงานบนสเตจที่เดือดปุด ๆ ยกเว้น addEventListenerอาร์กิวเมนต์สุดท้ายtrueซึ่งเป็นวิธีเดียวที่จะจับเหตุการณ์บนสเตจเก็บภาพ
  3. Bubbling / capture สามารถหยุดได้โดยevent.cancelBubble=true(IE) หรือevent.stopPropagation() สำหรับเบราว์เซอร์อื่น

7

นอกจากนี้ยังมีEvent.eventPhaseคุณสมบัติที่สามารถบอกคุณได้ว่าเหตุการณ์เป็นไปตามเป้าหมายหรือมาจากที่อื่น

โปรดทราบว่ายังไม่ได้กำหนดความเข้ากันได้ของเบราว์เซอร์ ฉันทดสอบบน Chrome (66.0.3359.181) และ Firefox (59.0.3) และรองรับที่นั่น

การขยายตัวอย่างที่ยอดเยี่ยมจากคำตอบที่ยอมรับแล้วนี่คือผลลัพธ์โดยใช้eventPhaseคุณสมบัติ

var logElement = document.getElementById('log');

function log(msg) {
  if (logElement.innerHTML == "<p>No logs</p>")
    logElement.innerHTML = "";
  logElement.innerHTML += ('<p>' + msg + '</p>');
}

function humanizeEvent(eventPhase){
  switch(eventPhase){
    case 1: //Event.CAPTURING_PHASE
      return "Event is being propagated through the target's ancestor objects";
    case 2: //Event.AT_TARGET
      return "The event has arrived at the event's target";
    case 3: //Event.BUBBLING_PHASE
      return "The event is propagating back up through the target's ancestors in reverse order";
  }
}
function capture(e) {
  log('capture: ' + this.firstChild.nodeValue.trim() + "; " + 
  humanizeEvent(e.eventPhase));
}

function bubble(e) {
  log('bubble: ' + this.firstChild.nodeValue.trim() + "; " + 
  humanizeEvent(e.eventPhase));
}

var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
  divs[i].addEventListener('click', capture, true);
  divs[i].addEventListener('click', bubble, false);
}
p {
  line-height: 0;
}

div {
  display:inline-block;
  padding: 5px;

  background: #fff;
  border: 1px solid #aaa;
  cursor: pointer;
}

div:hover {
  border: 1px solid #faa;
  background: #fdd;
}
<div>1
  <div>2
    <div>3
      <div>4
        <div>5</div>
      </div>
    </div>
  </div>
</div>
<button onclick="document.getElementById('log').innerHTML = '<p>No logs</p>';">Clear logs</button>
<section id="log"></section>


5

เดือด

  Event propagate to the upto root element is **BUBBLING**.

จับ

  Event propagate from body(root) element to eventTriggered Element is **CAPTURING**.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.