Focus & Blur
JavaScript DOM · Reference cheat sheet
Focus & Blur
JavaScript DOM · Reference cheat sheet
📋 Overview
Focus determines which element receives keyboard input. Manage it with focus(), blur(), tabIndex, autofocus, and focus events. Critical for accessibility, modals, and forms. Prefer moving focus intentionally; never trap keyboard users without an escape.
🔧 Core concepts
- Methods:
el.focus(\{ preventScroll? \}),el.blur(). - Events:
focus,blur(no bubble);focusin,focusout(bubble). document.activeElement: currently focused element.- Tab order: DOM order +
tabindex(0in order, positive avoid,-1programmatic only). - Focusable: links, buttons, inputs,
[tabindex], some custom withtabindex=0. :focus-visible: keyboard-ish focus styling (CSS).
input.focus();
input.addEventListener("focus", () => input.select());💡 Examples
// focusin delegation
form.addEventListener("focusin", (e) => {
e.target.closest(".field")?.classList.add("focused");
});
form.addEventListener("focusout", (e) => {
e.target.closest(".field")?.classList.remove("focused");
});
// Modal open: focus first control, restore later
let prev;
function openModal(modal) {
prev = document.activeElement;
modal.showModal?.() ?? modal.removeAttribute("hidden");
modal.querySelector("button, [href], input")?.focus();
}
function closeModal(modal) {
modal.close?.() ?? modal.setAttribute("hidden", "");
prev?.focus();
}
// Programmatic-only focus target
const panel = document.querySelector("#panel");
panel.tabIndex = -1;
panel.focus({ preventScroll: true });
// Detect focus leaving container
container.addEventListener("focusout", (e) => {
if (!container.contains(e.relatedTarget)) {
// focus left container
}
});
// Roving tabindex pattern (toolbar)
function setActive(items, active) {
items.forEach((el) => (el.tabIndex = el === active ? 0 : -1));
active.focus();
}// Don’t steal focus on every render
if (document.activeElement !== input) input.focus();⚠️ Pitfalls
focus/blurdon’t bubble — usefocusin/focusoutfor delegation.- Positive
tabindexcreates maintenance nightmares — prefer DOM order +0/-1. - Focusing hidden elements fails or confuses AT — show first, then focus.
preventScrollsupport matters for sticky headers.- Removing a focused node moves focus to
<body>— restore deliberately.
🔗 Related
- events.md — focus events
- forms_validation.md — form UX
- ../Html/dialog.md — modal focus
- ../Html/accessibility.md — a11y
- ../Html/aria.md — aria-activedescendant