Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Event Bubbling vs Event Capturing trong JavaScript

Luồng sự kiện DOM: Capturing → Target → Bubbling

Khi sự kiện xảy ra trên element, nó đi qua 3 giai đoạn:

  1. Capturing Phase (Giai đoạn bắt): Từ window → ... → parent → target.
  2. Target Phase (Giai đoạn mục tiêu): Tại chính element kích hoạt sự kiện.
  3. Bubbling Phase (Giai đoạn nổi bọt): Từ target → parent → ... → window.

Mặc định addEventListener lắng nghe ở **Bubbling Phase**. Truyền true (hoặc { capture: true }) để lắng nghe ở **Capturing Phase**.

Ví dụ minh họa

<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Event Bubbling vs Capturing</title>
    <style>
        body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; }
        .outer { 
            display: inline-block; width: 300px; height: 150px; 
            background: #7fff00; padding: 20px; margin: 10px; 
        }
        .inner { 
            width: 150px; height: 80px; 
            background: #8a2be2; color: white; 
            display: flex; align-items: center; justify-content: center;
            margin: 20px auto;
        }
        .tags { display: inline-block; width: 300px; font-weight: bold; margin: 10px 0; }
        .result { font-size: 18px; font-weight: 500; color: blueviolet; font-family: monospace; margin-top: 20px; }
    </style>
</head>
<body>
    <h1>Event Bubbling vs Event Capturing</h1>
    
    <!-- Bubbling (mặc định) -->
    <div class="outer" id="outer1"> OUTER (Bubbling)
        <div class="inner" id="inner1">INNER</div>
    </div>
    
    <!-- Capturing (capture: true) -->
    <div class="outer" id="outer2"> OUTER (Capturing)
        <div class="inner" id="inner2">INNER</div>
    </div>
    
    <br>
    <div class="tags">Bubbling (mặc định)</div>
    <div class="tags">Capturing (capture: true)</div>
    <div class="result" id="result"></div>

    <script>
        const outer1 = document.getElementById("outer1");
        const inner1 = document.getElementById("inner1");
        const outer2 = document.getElementById("outer2");
        const inner2 = document.getElementById("inner2");
        const result = document.getElementById("result");

        // BUBBLING (mặc định capture = false)
        outer1.addEventListener("click", () => {
            result.innerHTML += "🔵 OUTER (bubbling) clicked<br>";
        });
        inner1.addEventListener("click", () => {
            result.innerHTML = "";
            result.innerHTML += "🔴 INNER (bubbling) clicked<br>";
        });

        // CAPTURING (capture = true)
        outer2.addEventListener("click", () => {
            result.innerHTML = "";
            result.innerHTML += "🟢 OUTER (capturing) clicked<br>";
        }, true);
        inner2.addEventListener("click", () => {
            result.innerHTML += "🟡 INNER (capturing) clicked<br>";
        }, true);
    </script>
</body>
</html>

Kết quả khi click vào INNER div

// Bubbling (outer1/inner1):
🔴 INNER (bubbling) clicked
🔵 OUTER (bubbling) clicked

// Capturing (outer2/inner2):
🟢 OUTER (capturing) clicked
🟡 INNER (capturing) clicked

Dừng sự kiện lan truyền

element.addEventListener('click', (e) => {
    e.stopPropagation(); // Ngăn bubbling/capturing tiếp tục
    // e.stopImmediatePropagation(); // Cũng ngăn các listener khác trên CÙNG element
});

Event Delegation (Ứng dụng của Bubbling)

Gán 1 listener cho parent thay vì nhiều listener cho children:

<ul id="list">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>

<script>
    document.getElementById('list').addEventListener('click', (e) => {
        if (e.target.tagName === 'LI') {
            console.log('Clicked:', e.target.textContent);
        }
    });
</script>

Tóm tắt

  • Bubbling: Con → Cha → ... → Window (mặc định).
  • Capturing: Window → ... → Cha → Con (cần {capture: true}).
  • Target: Element thực sự kích hoạt event.
  • stopPropagation() dừng lan truyền.
  • Event Delegation tận dụng bubbling để gán ít listener hơn.