Skip to main content

The event.stopPropagation() method

event.stopPropagation() stops the event from travelling any further through the DOM tree: the event still fires on the element where it happened, but it never rises to that element's ancestors. The method only affects the event's route. It does not cancel the browser's default action and it does not block other handlers attached to the same element.

Theory

TL;DR

  • stopPropagation() cuts off further propagation through the tree: both bubbling upward and any remaining capturing downward.
  • Handlers attached to the same element still run: the method stops neighbours in the tree, not neighbours on the element.
  • It does not cancel the browser's default action (following a link, submitting a form), that is what preventDefault() is for.
  • stopImmediatePropagation() is stricter: it stops propagation and the remaining handlers on this very element.
  • Overusing it silently breaks event delegation: the parent handler simply stops receiving clicks.

Quick example

javascript
document.querySelector('.child').addEventListener('click', (event) => { console.log('child'); event.stopPropagation(); // stop bubbling, the parent will see nothing });

Event bubbling by default

Most DOM events bubble: after being handled on the target element they travel up the chain of ancestors.

html
<div class="parent"> <button class="child">Click</button> </div>
javascript
document.querySelector('.parent').addEventListener('click', () => { console.log('Parent'); }); document.querySelector('.child').addEventListener('click', () => { console.log('Child'); });

Clicking the .child button logs:

text
Child Parent

The handler on the button ran first, then the event bubbled up and triggered the parent's handler.

What stopPropagation() actually does

It is enough to call the method inside the child's handler:

javascript
document.querySelector('.child').addEventListener('click', (event) => { console.log('Child'); event.stopPropagation(); // stop the bubbling });

Now a click on the button logs only:

text
Child

The event never reaches the parent, it gets stuck on the button. The same holds for deeper nesting:

html
<div id="outer"> <div id="inner"> <button id="btn">Click me</button> </div> </div>
javascript
outer.addEventListener('click', () => console.log('outer')); inner.addEventListener('click', () => console.log('inner')); btn.addEventListener('click', (e) => { e.stopPropagation(); console.log('button'); });

A click on the button prints only button, while inner and outer never receive the event.

Delegation: when bubbling gets in the way

If a single shared handler sits on the parent (classic delegation), a click on a nested button sometimes must not reach it:

html
<ul id="list"> <li>Item 1 <button>Delete</button></li> <li>Item 2 <button>Delete</button></li> </ul>
javascript
list.addEventListener('click', (e) => { if (e.target.tagName === 'LI') { console.log('Item clicked'); } }); document.querySelectorAll('button').forEach(btn => btn.addEventListener('click', (e) => { e.stopPropagation(); // so the parent does not receive the event console.log('Deleting the item'); }) );

The result:

  • a click on <li> logs "Item clicked";
  • a click on the button logs "Deleting the item", and the event does not bubble to the list.

What stopPropagation() does not do

  • It does not cancel the browser's default action. The form will still submit, the link will still open. You need event.preventDefault() for that.
  • It does not block other handlers on the same element. If a button has three click listeners, all three run, in the order they were added.

stopImmediatePropagation() and the summary table

There is a stricter option:

javascript
event.stopImmediatePropagation();

It stops everything: propagation through the tree and the remaining handlers on this very element, so further processing is blocked completely.

MethodWhat it doesStops bubbling?Stops other handlers on the element?
event.stopPropagation()Interrupts propagation further through the treeYesNo
event.stopImmediatePropagation()Blocks any further processing of the eventYesYes
event.preventDefault()Cancels the browser's default actionNoNo

A short mnemonic: stopPropagation() means "do not go any higher", preventDefault() means "do not do the default thing", stopImmediatePropagation() means "stop everything, even the neighbours".

Common mistakes

  • Confusing it with preventDefault(). The first controls the event's route, the second controls the browser's default action. To both keep a form event from bubbling and keep it from submitting you need both calls.
  • Adding stopPropagation() just in case in every handler. That silently breaks event delegation and global listeners on document, for example closing a dropdown on an outside click.
  • Expecting it to disable the other handlers on the same element. It will not, that is what stopImmediatePropagation() is for.
  • Forgetting the capture phase. If the listener was added with { capture: true }, the call cuts the event off before it even reaches the target element.
  • Writing return false instead of the call. In a plain DOM handler that does nothing: this behaviour exists only in jQuery, where return false calls both methods at once.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.