- Home
- /
- Tutorials
- /
- JavaScript Tutorial
- /
- JavaScript Event Bubbling
JavaScript Events
JavaScript Event Bubbling
An event fired on an element travels up through its ancestors.
Click a button inside a div and the div hears about it too.
This is called bubbling, and it is what makes event delegation possible.
Example
Example
javascript
document.body.innerHTML =
'<div id="outer"><button id="inner">Go</button></div>';
document.getElementById("outer").addEventListener("click", function () {
console.log("outer heard it");
});
document.getElementById("inner").click();The button was clicked, but the div's handler ran.
How an Event Travels
First it goes down from the document to the target: the capturing phase.
Then it runs on the target itself.
Then it travels back up: the bubbling phase, where handlers normally run.
Syntax
Syntax
javascript
element.addEventListener("click", handler, true);A third argument of true listens during capturing instead.
The Order Handlers Run
Bubbling means the innermost handler runs first.
Example
Example
javascript
document.body.innerHTML =
'<div id="outer"><div id="middle"><button id="inner">Go</button></div></div>';
const order = [];
document.getElementById("outer").addEventListener("click", function () { order.push("outer"); });
document.getElementById("middle").addEventListener("click", function () { order.push("middle"); });
document.getElementById("inner").addEventListener("click", function () { order.push("inner"); });
document.getElementById("inner").click();
console.log(order.join(" "));The output is inner middle outer.
Capturing Runs First
A capturing listener hears the event on the way down.
Example
Example
javascript
document.body.innerHTML =
'<div id="outer"><button id="inner">Go</button></div>';
const order = [];
document.getElementById("outer").addEventListener("click", function () {
order.push("outer capturing");
}, true);
document.getElementById("inner").addEventListener("click", function () {
order.push("inner");
});
document.getElementById("inner").click();
console.log(order.join(" then "));The outer capturing handler ran before the target's own.
target Stays the Same
However far the event travels, target is still where it started.
currentTarget changes with each handler.
Example
Example
javascript
document.body.innerHTML =
'<div id="outer"><button id="inner">Go</button></div>';
document.getElementById("outer").addEventListener("click", function (event) {
console.log(event.target.id + " / " + event.currentTarget.id);
});
document.getElementById("inner").click();The output is inner / outer.
Stopping the Bubble
stopPropagation prevents the event travelling any further.
Use it sparingly; it can make other handlers mysteriously stop working.
Example
Example
javascript
document.body.innerHTML =
'<div id="outer"><button id="inner">Go</button></div>';
const order = [];
document.getElementById("outer").addEventListener("click", function () { order.push("outer"); });
document.getElementById("inner").addEventListener("click", function (event) {
order.push("inner");
event.stopPropagation();
});
document.getElementById("inner").click();
console.log(order.join(" "));Only inner ran, because the event never reached the div.
Not Everything Bubbles
focus and blur do not bubble.
focusin and focusout are the bubbling versions.
Example
Example
javascript
document.body.innerHTML = '<div id="box"><input id="field"></div>';
document.getElementById("box").addEventListener("focusin", function () {
console.log("box heard the focus");
});
document.getElementById("field").dispatchEvent(
new Event("focusin", { bubbles: true })
);Most events bubble, but it is worth checking when one seems not to.
Complete Example
Complete Example
html
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Event Bubbling</title>
<style>
#outer { padding: 24px; background: #dbeafe; }
#inner { padding: 12px; }
</style>
</head>
<body>
<h1>Event Bubbling</h1>
<div id="outer">
Outer
<button id="inner">Inner button</button>
</div>
<p id="out"></p>
<script>
const out = document.getElementById("out");
let log = "";
document.getElementById("inner").addEventListener("click", function () {
log = "inner";
out.textContent = log;
});
document.getElementById("outer").addEventListener("click", function () {
log = log + " then outer";
out.textContent = log;
});
</script>
</body>
</html>Try It Yourself
Run the above example in the Try It Editor.
Try stopping it:
Add event.stopPropagation() to the inner handler.
Important Points
- Events capture down, run on the target, then bubble up.
- Handlers run during bubbling unless you ask otherwise.
- The innermost handler runs first.
targetstays the same;currentTargetchanges.stopPropagationstops the event travelling further.
Conclusion
Bubbling explains why a parent hears about a click on its child.
It is not a quirk to work around but a feature to use.
The next lesson turns it into event delegation.
