Sự kiện sủi bọt và chụp là gì?


Câu trả lời:


1439

Sủi bọt và nắm bắt sự kiện là hai cách lan truyền sự kiện trong API DOM DOM, khi một sự kiện xảy ra trong một phần tử bên trong một phần tử khác và cả hai phần tử đã đăng ký xử lý cho sự kiện đó. Chế độ lan truyền sự kiện xác định theo thứ tự các yếu tố nhận sự kiện .

Với bong bóng, sự kiện đầu tiên được nắm bắt và xử lý bởi phần tử trong cùng và sau đó được truyền đến các phần tử bên ngoài.

Với việc chụp, sự kiện này được bắt trước bởi phần tử ngoài cùng và được truyền đến các phần tử bên trong.

Chụp cũng được gọi là "nhỏ giọt", giúp ghi nhớ thứ tự lan truyền:

nhỏ giọt xuống, bong bóng lên

Quay trở lại những ngày xưa, Netscape chủ trương nắm bắt sự kiện, trong khi Microsoft thúc đẩy sự kiện sôi nổi. Cả hai đều là một phần của tiêu chuẩn Sự kiện Mô hình Đối tượng Tài liệu W3C (2000).

IE <9 chỉ sử dụng bong bóng sự kiện , trong khi IE9 + và tất cả các trình duyệt chính hỗ trợ cả hai. Mặt khác, hiệu suất của sự kiện sủi bọt có thể thấp hơn một chút đối với các DOM phức tạp.

Chúng ta có thể sử dụng trình addEventListener(type, listener, useCapture)đăng ký xử lý sự kiện ở chế độ bong bóng (mặc định) hoặc chế độ chụp. Để sử dụng mô hình chụp, vượt qua đối số thứ ba là true.

Thí dụ

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

Trong cấu trúc trên, giả sử rằng một sự kiện nhấp chuột đã xảy ra trong liphần tử.

Trong mô hình chụp, sự kiện sẽ được xử lý bởi người divđầu tiên (nhấp vào xử lý sự kiện trong ý divchí sẽ bắn trước), sau đó ở phần cuối ul, sau đó ở phần cuối trong phần tử đích , li.

Trong mô hình bong bóng, điều ngược lại sẽ xảy ra: sự kiện đầu tiên sẽ được xử lý bởi li, sau đó bởi ulvà cuối cùng bởi divphần tử.

Để biết thêm thông tin, xem

Trong ví dụ dưới đây, nếu bạn nhấp vào bất kỳ yếu tố nào được tô sáng, bạn có thể thấy rằng giai đoạn bắt giữ của luồng lan truyền sự kiện xảy ra trước tiên, sau đó là giai đoạn sủi bọt.

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>

Một ví dụ khác tại JSFiddle .


41
useCapturehiện được hỗ trợ trong IE> = 9. nguồn
beatgammit

7
Tôi biết quá muộn để đưa ra nhận xét nhưng bài viết hay tôi tìm thấy ở đây catcode.com/domcontent/events/capture.html
Chỉ cần mã

3
triclklinggiống như capturing? Crockford nói về cuộc Trickling v. Bubblingtrò chuyện video này - youtube.com/watch?v=Fv9qT9joc0M&list=PL7664379246A246CB xung quanh 1 hr 5 minutes.
Kevin Meredith

1
@KevinMeredith Điều tương tự. "Trickling" chỉ giúp bạn dễ nhớ những gì hai mô hình làm (nhỏ giọt xuống , bong bóng lên ).
một con mèo

7
Câu trả lời trên đúng liên quan đến thứ tự trong phần giải thích chi tiết, nhưng khiến bạn nghĩ rằng nhỏ giọt xảy ra lần thứ hai với "bong bóng lên, nhỏ giọt xuống". Các sự kiện luôn trải qua giai đoạn chụp trước giai đoạn bong bóng. Thứ tự đúng là trickle down=> onElement=>bubble up
hết hạn

513

Sự miêu tả:

quirksmode.org có một mô tả hay về điều này. Tóm lại (được sao chép từ quirksmode):

Chụp sự kiện

Khi bạn sử dụng chụp sự kiện

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

trình xử lý sự kiện của Element1 kích hoạt trước, trình xử lý sự kiện của Element2 kích hoạt trước.

Sự kiện sủi bọt

Khi bạn sử dụng bong bóng sự kiện

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

trình xử lý sự kiện của Element2 kích hoạt trước, trình xử lý sự kiện của Element1 kích hoạt trước.


Dùng gì?

Nó phụ thuộc vào những gì bạn muốn làm. Không có tốt hơn. Sự khác biệt là thứ tự thực hiện của các trình xử lý sự kiện. Hầu hết thời gian sẽ ổn khi bắn những người xử lý sự kiện trong giai đoạn sủi bọt nhưng cũng có thể cần phải bắn họ sớm hơn.


Không phải cả hai xảy ra, đầu tiên là bắt và sau đó là bong bóng, còn sự kiện gửi là gì?
Suraj Jain

một ví dụ đồ họa ở đây: javascript.info/bubble-and-capturing
Cộng đồng Ans

71

Nếu có hai phần tử phần tử 1 và phần tử 2. Phần tử 2 nằm trong phần tử 1 và chúng tôi đính kèm một trình xử lý sự kiện với cả hai phần tử cho phép onClick. Bây giờ khi chúng ta nhấp vào phần tử 2 thì eventHandler cho cả hai phần tử sẽ được thực thi. Bây giờ ở đây câu hỏi là theo thứ tự sự kiện sẽ thực hiện. Nếu sự kiện được đính kèm với phần tử 1 thực thi trước thì nó được gọi là ghi sự kiện và nếu sự kiện được gắn với phần tử 2 thực thi trước thì đây được gọi là sự kiện sủi bọt. Theo W3C, sự kiện sẽ bắt đầu trong giai đoạn bắt giữ cho đến khi nó đến mục tiêu trở lại thành phần và sau đó nó bắt đầu sủi bọt

Các trạng thái bắt giữ và sủi bọt được biết đến bởi tham số useCapture của phương thức addEventListener

eventTarget.addEventListener (loại, người nghe, [, useCapture]);

Theo mặc định useCapture là sai. Nó có nghĩa là nó đang trong giai đoạn sủi bọt.

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>

Hãy thử với thay đổi đúng và sai.


2
@masterxilo: không cần Fiddle, StackOverflow hiện hỗ trợ mã nội tuyến (stack snippets) .
Dan Dascalescu

Về the event will start in the capturing phase untill it reaches the target comes back to the element and then it starts bubbling. Tôi chỉ tìm thấy addEventListener có tham số useCapturecó thể được đặt thành true hoặc false; và trong HTML 4.0, trình lắng nghe sự kiện được chỉ định là thuộc tính của một phần tửuseCapture defaults to false. Bạn có thể liên kết đến một thông số xác nhận những gì bạn đã viết?
lướt sóng

25

Tôi đã tìm thấy hướng dẫn này tại javascript.info rất rõ ràng trong việc giải thích chủ đề này. Và tóm tắt 3 điểm của nó ở cuối là thực sự nói về những điểm quan trọng. Tôi trích dẫn nó ở đây:

  1. Các sự kiện đầu tiên được nắm bắt xuống mục tiêu sâu nhất, sau đó nổi bong bóng. Trong IE <9 họ chỉ có bong bóng.
  2. Tất cả các trình xử lý làm việc trên sân khấu bong bóng chấp nhận addEventListenervới đối số cuối cùng true, đó là cách duy nhất để bắt sự kiện trên sân khấu chụp.
  3. Có thể dừng bong bóng / chụp bằng event.cancelBubble=true(IE) hoặc event.stopPropagation() cho các trình duyệt khác.

7

Ngoài ra còn có Event.eventPhasetài sản có thể cho bạn biết nếu sự kiện là mục tiêu hoặc đến từ một nơi khác.

Lưu ý rằng khả năng tương thích trình duyệt chưa được xác định. Tôi đã thử nghiệm nó trên Chrome (66.0.3359.181) và Firefox (59.0.3) và nó được hỗ trợ ở đó.

Mở rộng trên đoạn trích đã tuyệt vời từ câu trả lời được chấp nhận , đây là đầu ra sử dụng thuộc eventPhasetính

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

Bong bóng

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

Chụp

  Event propagate from body(root) element to eventTriggered Element is **CAPTURING**.
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.