JavaScript Events (Part 2) Propagation: Bubbling, Capturing, Deligation, Target and many more in depth
What is event propagation?
Event propagation refers to the process by which events are handled in a software application, particularly in the context of graphical user interfaces (GUIs) or web development. Events can be user actions, such as clicking a button or pressing a key, or system-generated events, like a timer expiring. Understanding event propagation is crucial for developing interactive and responsive applications.
There are two main phases in event propagation: the capturing phase and the bubbling phase. These phases describe the order in which the event is processed as it travels through the hierarchy of elements in the DOM (Document Object Model) in the case of web development.
In thiseventListeners
, we saw two arguments: one is the event, and the other is the callback function. But there is also a third argument, which is a boolean (true or false) value that causes the propagation of an event (event bubbling or event capturing). By default, the third argument is false.
What is event capturing?
Event capture is one phase of event propagation in the DOM that occurs when an event is triggered on an HTML element. Event capture happens during the first phase of this flow. Before the event reaches the target element, it goes through the capture phase. The event starts from the root of the DOM tree and moves towards the target element.
For example, if you have HTML documents given below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Understanding DOM Events</title>
<style>
body{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #212121;
}
body,#grandParent,#parent,#child{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 25px;
}
#grandParent{
width: 900px;
height: 650px;
background-color: rgb(17, 17, 17);
color: white;
margin-top: 50px;
}
#parent{
width: 700px;
height: 450px;
background-color: yellowgreen;
color: white;
margin-top: 20px;
color: black;
}
#child{
width: 500px;
height: 200px;
background-color: black;
color: white;
padding: 20px;
}
</style>
</head>
<body>
<div id="grandParent">
<h1>
Grand Parent Div
</h1>
<div id="parent">
<h1>
Parent Div
</h1>
<div id="child">
<h1>
Child Div
</h1>
</div>
</div>
</div>
</body>
</html>
Then, in a JavaScript file, you have to write this code to understand the capture of the event
let grandParent = document.getElementById('grandParent');
let parent = document.getElementById('parent');
let child = document.getElementById('child');
grandParent.addEventListener('click', ()=>{
alert('Grand Parent div is called');
}, true);
parent.addEventListener('click', ()=>{
alert('Parent div is called');
}, true);
child.addEventListener('click', ()=>{
alert('Child div is called');
}, true);
In the above JS code, we will see the third boolean argument. If that argument is true, then the event starts from the root of the DOM tree and moves towards the target element, meaning if you click on the child element, then the Grand Parent Div
calls first, then the Parent Div
calls, and then the Child Div
calls. It comes from the top to the bottom of the web page.
What is event bubbling?
Event bubbling is a phase in the event propagation model of the DOM that occurs when an event is triggered on an HTML element. Event bubbling happens during the second phase of this flow. After the event reaches the target, it enters the bubbling phase. The event then starts to bubble up from the target element towards the root of the DOM tree. During this phase, the event is propagated through the ancestors of the target element.
For example, if you have HTML documents given below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Understanding DOM Events</title>
<style>
body{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #212121;
}
body,#grandParent,#parent,#child{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 25px;
}
#grandParent{
width: 900px;
height: 650px;
background-color: rgb(17, 17, 17);
color: white;
margin-top: 50px;
}
#parent{
width: 700px;
height: 450px;
background-color: yellowgreen;
color: white;
margin-top: 20px;
color: black;
}
#child{
width: 500px;
height: 200px;
background-color: black;
color: white;
padding: 20px;
}
</style>
</head>
<body>
<div id="grandParent">
<h1>
Grand Parent Div
</h1>
<div id="parent">
<h1>
Parent Div
</h1>
<div id="child">
<h1>
Child Div
</h1>
</div>
</div>
</div>
</body>
</html>
Then, in a JavaScript file, you have to write this code to understand the bubbling of the event
let grandParent = document.getElementById('grandParent');
let parent = document.getElementById('parent');
let child = document.getElementById('child');
grandParent.addEventListener('click', ()=>{
alert('Grand Parent div is called');
}, false);
parent.addEventListener('click', ()=>{
alert('Parent div is called');
}, false);
child.addEventListener('click', ()=>{
alert('Child div is called');
}, false);
In the above JS code, we will see the third boolean argument. If that argument is false, then the event starts from the target element and moves towards the root of the DOM tree, meaning if you click on the child element, then the Child Div
calls first, then the Parent Div
calls, and then the Grand Parent Div
calls. It comes from the bottom to the top of the web page. By default, it is false. If you don't write the argument as false, then it is considered false.
You can also understand the event propagation concept with the help of the image given below.
What is event delegation?
Event delegation is a programming concept often used in web development, particularly in DOM manipulation. It involves handling events on a parent element rather than on the individual child elements that might trigger the event. It means you don't need to put event listeners individually on every single element; you just put event listeners on the parent or grandparent element. This technique offers several advantages, including improved performance and simplification of code, especially in situations where there are many dynamically created elements.
For example, if you have HTML documents given below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Understanding DOM Events</title>
<style>
body{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #212121;
}
body,#grandParent,#parent,#child{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 25px;
}
#grandParent{
width: 900px;
height: 650px;
background-color: rgb(17, 17, 17);
color: white;
margin-top: 50px;
}
#parent{
width: 700px;
height: 450px;
background-color: yellowgreen;
color: white;
margin-top: 20px;
color: black;
}
#child{
width: 500px;
height: 200px;
background-color: black;
color: white;
padding: 20px;
}
</style>
</head>
<body>
<div id="grandParent">
<h1>
Grand Parent Div
</h1>
<div id="parent">
<h1>
Parent Div
</h1>
<div id="child">
<h1>
Child Div
</h1>
</div>
</div>
</div>
</body>
</html>
Then, in a JavaScript file, you have to write this code to understand the bubbling of the event
let grandParent = document.getElementById('grandParent');
grandParent.addEventListener('click', ()=>{
alert('Grand Parent div is called');
}, false);
In the above code, we add a click event for just the grandparent div. When you click on a child element, the browser tries to find the event on the child; if it finds nothing there, then the browser bubbles up and goes toward its parent element, and if there is nothing, then it bubbles up to its grandparent element and finds the event. Once it finds it, it calls its callback function. This is the event delegation.
target
Thetarget
property represents the element on which the event was originally triggered or the element that triggered the event. This property returns the reference to the DOM element that was the source of the event.
In the event delegation, we saw that we added a click event for just the grandparent div. What if you want to access the child, parent, and grandparent divisions individually? This could be done by event objects that have a target and current target properties.
For example, if you have HTML documents given below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Understanding DOM Events</title>
<style>
body{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #212121;
}
body,#grandParent,#parent,#child{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 25px;
}
#grandParent{
width: 900px;
height: 650px;
background-color: rgb(17, 17, 17);
color: white;
margin-top: 50px;
}
#parent{
width: 700px;
height: 450px;
background-color: yellowgreen;
color: white;
margin-top: 20px;
color: black;
}
#child{
width: 500px;
height: 200px;
background-color: black;
color: white;
padding: 20px;
}
</style>
</head>
<body>
<div id="grandParent">
<h1>
Grand Parent Div
</h1>
<div id="parent">
<h1>
Parent Div
</h1>
<div id="child">
<h1>
Child Div
</h1>
</div>
</div>
</div>
</body>
</html>
For example, in a JavaScript file, you have to write this code to understand the object of the event( we write it as e).
let grandParent = document.getElementById('grandParent');
grandParent.addEventListener('click', (e)=>{
console.log(e.target);
}, false);
After applying this code, when you click on a child, in the console, you can see that you clicked on the child element only. and the same for the parent and grandparent div.
currentTarget
ThecurrentTarget
property represents the element to which the event handler is currently attached. This property remains constant throughout the event propagation, regardless of where the event originated. It is particularly useful when dealing with event delegation, where a common ancestor is used to listen for events on multiple child elements.
For example, if you have HTML documents given below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Understanding DOM Events</title>
<style>
body{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #212121;
}
body,#grandParent,#parent,#child{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 25px;
}
#grandParent{
width: 900px;
height: 650px;
background-color: rgb(17, 17, 17);
color: white;
margin-top: 50px;
}
#parent{
width: 700px;
height: 450px;
background-color: yellowgreen;
color: white;
margin-top: 20px;
color: black;
}
#child{
width: 500px;
height: 200px;
background-color: black;
color: white;
padding: 20px;
}
</style>
</head>
<body>
<div id="grandParent">
<h1>
Grand Parent Div
</h1>
<div id="parent">
<h1>
Parent Div
</h1>
<div id="child">
<h1>
Child Div
</h1>
</div>
</div>
</div>
</body>
</html>
For example, in a JavaScript file, you have to write this code to understand the object of the event( we write it as e).
let grandParent = document.getElementById('grandParent');
grandParent.addEventListener('click', (e)=>{
console.log(e.currentTarget);
}, false);
After applying this code, when you click on a child, parent, or grandparent in the console, you can see that you clicked on the grandparent element only because the event is added to that div.
Let's talk about some other properties of the event
srcElement
srcElement
is a property that was historically used in the DOM to refer to the element that triggered an event. However, it's important to note that thissrcElement
is a non-standard property and was primarily used in Internet Explorer.
For example, if you have HTML documents given below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Understanding DOM Events</title>
<style>
body{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #212121;
}
body,#grandParent,#parent,#child{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 25px;
}
#grandParent{
width: 900px;
height: 650px;
background-color: rgb(17, 17, 17);
color: white;
margin-top: 50px;
}
#parent{
width: 700px;
height: 450px;
background-color: yellowgreen;
color: white;
margin-top: 20px;
color: black;
}
#child{
width: 500px;
height: 200px;
background-color: black;
color: white;
padding: 20px;
}
</style>
</head>
<body>
<div id="grandParent">
<h1>
Grand Parent Div
</h1>
<div id="parent">
<h1>
Parent Div
</h1>
<div id="child">
<h1>
Child Div
</h1>
</div>
</div>
</div>
</body>
</html>
For example, in a JavaScript file, you have to write this code to understand the srcElement of the event property( we write it as e).
let grandParent = document.getElementById('grandParent');
grandParent.addEventListener('click', (e)=>{
console.log(e.srcElement);
}, false);
Note:
In modern web development, it's recommended to use the standard property target
instead, which is supported across various browsers. Thetarget
property also refers to the element that triggered the event.
If you are dealing with older code or a specific environment where srcElement
is still used, you should be aware that it may not work in all browsers, and it's generally better to update the code to use the more widely supported target
property.
stopPropagation()
In the context of the DOM in web development, the stopPropagation
method is used to prevent the further propagation of an event through the DOM hierarchy. When an event occurs on a particular element, it can trigger handlers not only on that element but also on its ancestors or descendants, depending on the event phase (capturing or bubbling).
For example, if you have HTML documents given below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Understanding DOM Events</title>
<style>
body{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #212121;
}
body,#grandParent,#parent,#child{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 25px;
}
#grandParent{
width: 900px;
height: 650px;
background-color: rgb(17, 17, 17);
color: white;
margin-top: 50px;
}
#parent{
width: 700px;
height: 450px;
background-color: yellowgreen;
color: white;
margin-top: 20px;
color: black;
}
#child{
width: 500px;
height: 200px;
background-color: black;
color: white;
padding: 20px;
}
</style>
</head>
<body>
<div id="grandParent">
<h1>
Grand Parent Div
</h1>
<div id="parent">
<h1>
Parent Div
</h1>
<div id="child">
<h1>
Child Div
</h1>
</div>
</div>
</div>
</body>
</html>
In a JavaScript file, if you have this code then
let grandParent = document.getElementById('grandParent');
let child = document.getElementById('child');
grandParent.addEventListener('click', (e)=>{
console.log('Divisions are clicked');
});
child.addEventListener('click', (e)=>{
console.log('Child div is clicked');
});
After applying this JavaScript code, when you click on the grandparent or parent div then in the console message appears that (Divisions are clicked
) but when you click on the child element then two messages appear: first is (Child div is clicked
) and then the event is bubbled up and a second message appears instantly (Divisions are clicked
). Sometimes you don't need this propagation or bubbling in the event so you can stop it by using stopPropagation()
on the targetted event.
So, you have to write this code to understand the concept stopPropagation()
of the event( we write it as e). After writing this code if you click on the child element then in the console it only shows that (Child div is clicked
) and the event is not bubbled up.
let grandParent = document.getElementById('grandParent');
let child = document.getElementById('child');
grandParent.addEventListener('click', (e)=>{
console.log('Divisions are clicked');
});
child.addEventListener('click', (e)=>{
console.log('Child div is clicked');
e.stopPropagation(); // This will stop bubbling on this event
});
preventDefault()
In the DOM, preventDefault()
is a method that is commonly used in the context of handling events. It is part of the Event interface and is used to stop the default action associated with an event from occurring. When an event occurs on an HTML element (e.g., a button click, or a form submission), the browser performs a default action associated with that event. For example, clicking on a link navigates to the URL specified in the link's href
attribute, or submitting a form sends data to the server.
For example, if you have HTML documents given below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Understanding DOM Events</title>
<style>
body{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #212121;
height: 700px;
}
body,#grandParent{
display: grid;
place-items: center;
border-radius: 25px;
}
#grandParent{
width: 500px;
height: 350px;
background-color: rgb(17, 17, 17);
color: white;
}
</style>
</head>
<body>
<div id="grandParent">
<ul>
<li>Home</li>
<li>About</li>
<li><a href="https://www.google.com/" id="href">Google</a></li>
</ul>
</div>
</body>
</html>
In a JavaScript file, if you have this code then if you click on Google
no action is being performed.
let href = document.getElementById('href');
href.addEventListener('click', (e)=>{
console.log('href is clicked');
e.preventDefault();
});