JavaScript Focusin vs Focus Event Bubbling Explained
In JavaScript, managing keyboard and form focus typically involves
two pairs of events: focus/blur and
focusin/focusout. While both pairs indicate
when an element receives or loses user focus, their primary difference
lies in event propagation. The focusin and
focusout events bubble up through the DOM tree, whereas
focus and blur do not bubble. Understanding
this distinction is essential for handling form validation, managing
complex UI components, and implementing efficient event delegation.
The Mechanism: Bubbling vs. Non-Bubbling
When a DOM event bubbles, it triggers on the target element first and
then ascends through its ancestor elements all the way to the
document and window objects.
focusandblur: These are non-bubbling events. When an<input>element gains focus, thefocusevent fires exclusively on that specific<input>. An event listener placed on a parent container, such as a<form>or<div>, will not detect thefocusevent during the standard bubbling phase.focusinandfocusout: These events support bubbling. When an<input>gains focus, thefocusinevent fires on the input and immediately propagates up to its parent elements. Any ancestor with afocusinlistener will capture the event as it travels upward.
Event Delegation
The ability to bubble makes focusin and
focusout the preferred choice for event delegation.
Without bubbling, tracking focus across multiple form fields requires
attaching individual focus listeners to every input
element, which increases memory overhead:
// Inefficient approach with focus
const inputs = document.querySelectorAll('form input');
inputs.forEach(input => {
input.addEventListener('focus', handleFocus);
});Because focusin bubbles, you can attach a single event
listener to the parent <form> element to manage focus
states across all child elements dynamically:
// Efficient approach using focusin delegation
const form = document.querySelector('form');
form.addEventListener('focusin', (event) => {
console.log('Focused element:', event.target);
});Capturing focus and
blur
Although focus and blur do not bubble, they
can still be captured by parent elements during the DOM event
capture phase. By setting the useCapture
parameter (the third argument in addEventListener) to
true, a parent element can intercept focus and
blur before they reach the target:
const form = document.querySelector('form');
form.addEventListener('focus', handleFocus, true); // Intercepts during capture phaseHowever, using focusin and focusout is
generally considered cleaner and more idiomatic since it relies on
standard bubbling behavior rather than altering the phase propagation
flow.
Event Execution Order
When focus moves from one element to another, the browser fires these four events in a specific sequence:
blur: Dispatched at the element losing focus (does not bubble).focusout: Dispatched at the element losing focus (bubbles).focus: Dispatched at the element gaining focus (does not bubble).focusin: Dispatched at the element gaining focus (bubbles).
Summary
Use focus and blur when you need to handle
focus logic tied strictly to a single, specific element. Use
focusin and focusout whenever you need event
delegation, parent-level tracking, or dynamic UI state updates across
multiple child elements.