Focusin vs Focus and Blur Event Bubbling in JavaScript
Understanding how focus-related events propagate through the Document
Object Model (DOM) is essential for effective event handling in
JavaScript. While focus, blur,
focusin, and focusout all detect when an
element gains or loses focus, their primary distinction lies in event
propagation: focusin and focusout bubble up
the DOM tree, whereas focus and blur do not.
This fundamental difference determines whether you can use standard
event delegation or need to rely on event capturing.
The Core Difference: Event Bubbling
In DOM event propagation, bubbling allows an event triggered on a child element to pass upward through its ancestor elements.
focusandblur: These events have theirbubblesproperty set tofalse. When an input field receives focus, thefocusevent fires exclusively on that specific element. Ancestor elements (such as a parent<div>or<form>) will not receive the event during the bubbling phase.focusinandfocusout: These events have theirbubblesproperty set totrue. When an element gains or loses focus, the event triggers on the target element and then bubbles up through each parent node to thedocumentandwindow.
Event Delegation
Because focusin and focusout bubble, they
support event delegation. Instead of attaching individual event
listeners to every interactive element inside a container, you can
attach a single listener to a common ancestor.
const form = document.querySelector('form');
// Works: focusin bubbles from child inputs up to the form
form.addEventListener('focusin', (event) => {
event.target.classList.add('active-input');
});
form.addEventListener('focusout', (event) => {
event.target.classList.remove('active-input');
});Attempting the same pattern with focus or
blur using standard bubbling listeners will fail because
the parent <form> never receives the bubbling
event.
Handling
focus and blur with Event Capturing
If you must use focus or blur at a parent
level, you cannot rely on bubbling. Instead, you must intercept the
event during the capture phase (as the event travels
down from the window to the target) by setting the
useCapture argument to true:
const form = document.querySelector('form');
// Capturing phase allows parent to intercept 'focus'
form.addEventListener('focus', (event) => {
event.target.classList.add('active-input');
}, true);Firing Order
When focus shifts from Element A to Element B, the browser executes the events in a specific sequence:
focusoutfires on Element A (bubbles).focusinfires on Element B (bubbles).blurfires on Element A (does not bubble).focusfires on Element B (does not bubble).
focusin and focusout trigger before their
non-bubbling counterparts (focus and blur),
making them the preferred choice for modern event delegation patterns in
forms and interactive components.