Event Propagation and Bubbling
In this page:
$(selector).on("eventName", function (event) {
event.stopPropagation();
});
Bubbling
An event fired on a child element travels upward through its ancestor elements unless something stops it -- this upward travel is called bubbling.
Example: Bubbling
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<style>
button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
#resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
#output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
#demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
#demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
#demo :where(table) { border-collapse:collapse; }
#demo :where(td, th) { border-radius:0; }
#demo :where(input, select, textarea) { margin:4px 0; }
</style>
</head>
<body>
<button id="resetBtn">Reset</button>
<section id="demo">
<div id="outer"><button id="inner">Click</button></div>
</section>
<div id="output"></div>
<script>
var original = $("#demo").html();
function init() {
$("#outer").on("click", function() { $("#output").append($("<div>").text("Outer handler ran")); });
$("#inner").on("click", function() { $("#output").append($("<div>").text("Inner handler ran")); });
}
init();
$("#resetBtn").click(function() {
$("#demo").html(original);
$("#output").empty();
init();
});
</script>
</body>
</html>
Stop Propagation
Calling event.stopPropagation() inside a handler stops the event from continuing upward, so any handlers attached to parent elements for that same event never run.
Example: Stop Propagation
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<style>
button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
#resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
#output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
#demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
#demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
#demo :where(table) { border-collapse:collapse; }
#demo :where(td, th) { border-radius:0; }
#demo :where(input, select, textarea) { margin:4px 0; }
</style>
</head>
<body>
<button id="resetBtn">Reset</button>
<section id="demo">
<div id="outer"><button id="inner">Click</button></div>
</section>
<div id="output"></div>
<script>
var original = $("#demo").html();
function init() {
$("#outer").on("click", function() { $("#output").append($("<div>").text("Outer handler ran")); });
$("#inner").on("click", function(event) {
event.stopPropagation();
$("#output").append($("<div>").text("Inner handler ran, stopped bubbling"));
});
}
init();
$("#resetBtn").click(function() {
$("#demo").html(original);
$("#output").empty();
init();
});
</script>
</body>
</html>
Parent and Child
Because of bubbling, both a child element's own handler and any handler on its ancestors can respond to the same single click, running in order from the innermost element outward.
Example: Parent and Child
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<style>
button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
#resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
#output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
.btn { display:inline-block; padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border-radius:4px; cursor:pointer; font:13px sans-serif; }
#resetBtn.btn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
#demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
#demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
#demo :where(table) { border-collapse:collapse; }
#demo :where(td, th) { border-radius:0; }
#demo :where(input, select, textarea) { margin:4px 0; }
</style>
</head>
<body>
<span id="resetBtn" class="btn" role="button">Reset</span>
<section id="demo">
<div id="card"><button id="btn">Click</button></div>
</section>
<div id="output"></div>
<script>
var original = $("#demo").html();
function init() {
$("#btn").on("click", function() { $("#output").append($("<div>").text("1: button handler")); });
$("#card").on("click", function() { $("#output").append($("<div>").text("2: card handler")); });
}
init();
$("#resetBtn").click(function() {
$("#demo").html(original);
$("#output").empty();
init();
});
</script>
</body>
</html>
Prevent Bubbling
stopPropagation() is the tool to reach for specifically when a child element's own action -- like a button inside a clickable card -- shouldn't also trigger whatever the card's own click handler does.
Example: Prevent Bubbling
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<style>
button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
#resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
#output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
#demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
#demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
#demo :where(table) { border-collapse:collapse; }
#demo :where(td, th) { border-radius:0; }
#demo :where(input, select, textarea) { margin:4px 0; }
</style>
</head>
<body>
<button id="resetBtn">Reset</button>
<section id="demo">
<div id="card">Card <button id="btn">Delete</button></div>
</section>
<div id="output"></div>
<script>
var original = $("#demo").html();
function init() {
$("#card").on("click", function() { $("#output").append($("<div>").text("Card clicked")); });
$("#btn").on("click", function(event) {
event.stopPropagation();
$("#output").append($("<div>").text("Button deleted, card click suppressed"));
});
}
init();
$("#resetBtn").click(function() {
$("#demo").html(original);
$("#output").empty();
init();
});
</script>
</body>
</html>
Delegated Events
Event delegation deliberately relies on bubbling rather than fighting it: instead of attaching a handler to every child, you attach one handler to a shared parent and use the bubbled event's target to figure out which child was actually interacted with.
Example: Delegated Events
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<style>
button { padding:6px 14px; margin:4px; background:#2c6aa0; color:white; border:none; border-radius:4px; cursor:pointer; }
#resetBtn { background:white; color:#2c6aa0; border:1px solid #2c6aa0; }
#output { margin-top:10px; padding:8px; border:1px solid #ccc; border-radius:4px; background:#f7f7f7; font-family:monospace; min-height:24px; }
#demo :where(p, div, span, li, h1, h2, h3, h4, td, th, blockquote, label, section, article, aside) { border:1px solid #4a7fb5; background:#eaf2fb; padding:6px 10px; margin:4px 0; border-radius:4px; }
#demo :where(ul, ol) { border:1px dashed #888; background:#fafafa; padding:8px 8px 8px 32px; margin:6px 0; }
#demo :where(table) { border-collapse:collapse; }
#demo :where(td, th) { border-radius:0; }
#demo :where(input, select, textarea) { margin:4px 0; }
</style>
</head>
<body>
<button id="resetBtn">Reset</button>
<section id="demo">
<ul id="list"><li>Item 1</li></ul>
</section>
<div id="output"></div>
<script>
var original = $("#demo").html();
function init() {
$("#list").on("click", "li", function(event) {
$("#output").append($("<div>").text("Clicked: " + event.target.textContent));
});
}
init();
$("#resetBtn").click(function() {
$("#demo").html(original);
$("#output").empty();
init();
});
</script>
</body>
</html>
- Being surprised that clicking the inner button also runs the outer
#outerhandler, because events bubble up to every ancestor by default. - Calling
event.stopPropagation()and assuming it also cancels the default action, such as a link navigating, when that needsevent.preventDefault(). - Using
stopPropagation()to fix a stray handler, which also blocks delegated handlers and other code higher up that legitimately needs the event.
Chapter Quiz — Complete all 11 topics to unlock
0/11 topics done
Complete these topics first: