Event Bubbling in JavaScript: Understanding Event Propagation


Event bubbling is a mechanism in JavaScript where an event triggered on an element is propagated or "bubbles up" through its parent elements in the DOM hierarchy. It follows the natural hierarchy of the DOM, starting from the target element and moving up towards the topmost parent element.

When an event is triggered on an element, such as a click event, it goes through two phases: capturing and bubbling. In the capturing phase, the event starts from the topmost parent element (usually the <html> element) and traverses down to the target element. After the capturing phase, the event enters the bubbling phase, where it triggers on the target element and then propagates up through its parent elements.

During the bubbling phase, the event triggers on each parent element in the DOM hierarchy, which means that you can also attach event listeners to the parent or any ancestor elements to listen for the same event.

Event bubbling is the default behavior in most modern browsers, and it allows for convenient event handling. It enables you to listen for events on a parent element and handle events from multiple child elements within it. This approach is particularly useful when you have a list or a group of elements where you want to apply the same event handling logic without attaching individual event listeners to each child element.

Event delegation, as explained earlier, takes advantage of event bubbling by attaching an event listener to a parent element and utilizing the bubbling phase to handle events on its child elements.

Example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="style.css" />
    <link rel="icon" type="image/ico" href="logo.png" />
    <title>Document</title>
    <style>
      div {
        padding: 30px;
        margin: 10px;
        border: 3px solid #333;
        text-align: center;
      }
    </style>
  </head>
  <body>
    <h3>Event bubbling | Event Capturing</h3>
    <div id="grandparent">
      grandparent
      <div id="parent">
        parent
        <div id="child">child</div>
      </div>
    </div>
    <script src="event_bub.js"></script>
  </body>
</html>

event_bub.js

This code demonstrates event handling using event bubbling and event capturing phases in JavaScript. Let's go through the code and understand its behavior.

const grandparent = document.querySelector("#grandparent");
const parent = document.querySelector("#parent");
const child = document.querySelector("#child");

Here, three elements with IDs "grandparent," "parent," and "child" are selected from the DOM and stored in variables.

grandparent.addEventListener("click", function () {
  console.log("Grandparent Clicked");
}, true);

An event listener is attached to the grandparent element with the "click" event. The listener function logs "Grandparent Clicked" to the console. The third argument, true, indicates that the listener is registered for the capturing phase.

parent.addEventListener("click", function () {
  console.log("Parent Clicked");
}, false);

An event listener is attached to the parent element with the "click" event. The listener function logs "Parent Clicked" to the console. The third argument, false, indicates that the listener is registered for the bubbling phase.

child.addEventListener("click", function (e) {
  console.log("Child Clicked");
  e.stopImmediatePropagation();
}, true);

An event listener is attached to the child element with the "click" event. The listener function logs "Child Clicked" to the console. The third argument, true, indicates that the listener is registered for the capturing phase. Additionally, e.stopImmediatePropagation() is called to prevent further propagation of the event to other elements.

child.addEventListener("click", function (e) {
  console.log("Child Clicked2");
}, true);

Another event listener is attached to the child element with the "click" event. The listener function logs "Child Clicked2" to the console. The third argument, true, indicates that the listener is registered for the capturing phase.

In this code, event listeners are registered on the grandparent, parent, and child elements with different capturing/bubbling configurations. By clicking on different elements, you can observe the order of event execution based on the capturing and bubbling phases.

Based on the current commented configuration, the order of events logged would be:

  • Grandparent Clicked (capturing phase)
  • Parent Clicked (capturing phase)
  • Child Clicked (capturing phase)
  • Child Clicked2 (capturing phase)

However, if you uncomment the final configuration where e.stopImmediatePropagation() is used, only "Child Clicked" will be logged, and "Child Clicked2" won't be logged due to immediate propagation stoppage.

The behavior of event bubbling and capturing allows you to handle events on different elements and control the flow of event propagation in the DOM hierarchy.

Example - 2

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Modal</title>
    <style>
      @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700;900&display=swap");
      * {
        font-family: "Poppins", sans-serif;
      }

      #modal {
        position: fixed;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5);
        top: 0;
        left: 0;
        display: none;
        align-items: center;
        justify-content: center;
      }
      #modalContent {
        background-color: white;
        padding: 20px;
        border-radius: 5px;
      }
      #modalContent h2 {
        font-weight: 600;
      }
    </style>
  </head>
  <body>
    <button id="btnModal">Open Modal</button>
    <div id="modal">
      <div id="modalContent">
        <h2>Modal Heading</h2>
        <p>This is a modal window example.</p>
        <input type="text" id="txtName" placeholder="name" />
        <button id="btnSubmit">Submit</button>
      </div>
    </div>

    <script>
      const btnModal = document.querySelector("#btnModal");
      const modal = document.querySelector("#modal");
      const btnSubmit = document.querySelector("#btnSubmit");
      const txtName = document.querySelector("#txtName");

      btnModal.addEventListener("click", function () {
        modal.style.display = "flex";
      });
      modal.addEventListener("click", function () {
        modal.style.display = "none";
      });

      btnSubmit.addEventListener("click", function (e) {
        e.stopPropagation();
        console.log("Submit button Pressed");
      });
      txtName.addEventListener("click", function (e) {
        e.stopPropagation();
        console.log("Input Click");
      });
    </script>
  </body>
</html>

This code demonstrates a simple modal behavior with event handling in JavaScript. Let's go through the code and understand its functionality.

const btnModal = document.querySelector("#btnModal");
const modal = document.querySelector("#modal");
const btnSubmit = document.querySelector("#btnSubmit");
const txtName = document.querySelector("#txtName");

Here, elements with IDs "btnModal," "modal," "btnSubmit," and "txtName" are selected from the DOM and stored in variables.

btnModal.addEventListener("click", function () {
  modal.style.display = "flex";
});

An event listener is attached to the btnModal element with the "click" event. When the button is clicked, the listener function is triggered, and it sets the display property of the modal element to "flex", making the modal visible.

modal.addEventListener("click", function () {
  modal.style.display = "none";
});

An event listener is attached to the modal element with the "click" event. When the modal is clicked, the listener function is triggered, and it sets the display property of the modal element to "none", hiding the modal.

btnSubmit.addEventListener("click", function (e) {
  e.stopPropagation();
  console.log("Submit button Pressed");
});

An event listener is attached to the btnSubmit element with the "click" event. When the submit button is clicked, the listener function is triggered, and it logs "Submit button Pressed" to the console. Additionally, e.stopPropagation() is called to prevent further propagation of the event, ensuring that the modal doesn't close when clicking on the submit button.

txtName.addEventListener("click", function (e) {
  e.stopPropagation();
  console.log("Input Click");
});

An event listener is attached to the txtName element with the "click" event. When the input field is clicked, the listener function is triggered, and it logs "Input Click" to the console. Similarly, e.stopPropagation() is called to prevent further propagation of the event.

In summary, this code sets up a modal behavior where clicking the "Open Modal" button displays the modal, and clicking anywhere outside the modal closes it. The submit button and input field inside the modal have event listeners with e.stopPropagation() to prevent the modal from closing when interacting with them.

The code showcases how event handling can be used to control the behavior of a modal and capture specific actions within the modal without affecting the overall modal functionality.

List of Programs


JS Practical Questions & Answers


JS Practical Project