Let's pretend that we have the following DOM structure with 3 divs: grandparent, parent and child.
All 3 divs have their custom event handlers, like follows:
grandparent.addEventListener('click', () => console.log('Grandparent'));
parent.addEventListener('click', () => console.log('Parent'));
child.addEventListener('click', () => console.log('Child'));
If we click on the inner div (the child div), we would expect to call the child div's event handler, but actually we will see in the console that all 3 event handlers have been called, in the following order:
Let's understand why this happens.
- Capturing phase
- the DOM tree is parsed downwards until the caller element is found. Throughout its way, if there are nodes that explicitly require event execution in the capturing phase, their event handlers will be executed right away.
- Targeting phase
- the caller element that triggered the event is being targeted
- Bubbling phase
- the DOM tree is now parsed upwards until finding the document element, starting from the targeted/caller element. If on the way up we find elements with event handlers, they will be automatically called.
If we want to stop this behaviour of calling parent elements event handlers, we can use event.stopPropagation() on our last event handler that we want to be executed.