События фокуса: focus, blur
focusсрабатывает когда элемент получает фокус (с клавиатуры или кликом),blur— когда теряет; оба не всплывают, но их всплывающие аналогиfocusin/focusoutдоступны для делегирования.
Зачем нужно
Управление фокусом критично для доступности (a11y) и UX форм. Событие focus используется для подсветки активного поля, blur — для валидации после ввода. Без корректной обработки фокуса приложение неудобно для пользователей клавиатуры и скринридеров.
Где используется
- Валидация поля после заполнения (по
blur) - Подсветка активного поля ввода
- Показ/скрытие подсказок (плейсхолдеры, подписи)
- Автофокус при открытии модального окна
- Управление фокусом в SPA при переходах
Базовые события
const input = document.querySelector('#name-input');
input.addEventListener('focus', () => {
console.log('Поле получило фокус');
input.parentElement.classList.add('focused');
});
input.addEventListener('blur', () => {
console.log('Поле потеряло фокус');
input.parentElement.classList.remove('focused');
validateField(input); // валидируем после ввода
});
focus vs focusin (всплытие)
const form = document.querySelector('form');
// focus НЕ всплывает — нельзя делегировать обычным способом
form.addEventListener('focus', () => console.log('focus на form')); // не сработает для input
// focusin всплывает — можно использовать делегирование
form.addEventListener('focusin', (e) => {
console.log('Фокус получил:', e.target.name);
e.target.closest('.field')?.classList.add('active');
});
form.addEventListener('focusout', (e) => {
console.log('Фокус потерял:', e.target.name);
e.target.closest('.field')?.classList.remove('active');
});
Валидация в реальном времени
function setupFieldValidation(input, validator, errorMessage) {
const errorEl = document.querySelector(`#${input.id}-error`);
input.addEventListener('blur', () => {
const isValid = validator(input.value);
input.classList.toggle('invalid', !isValid);
input.classList.toggle('valid', isValid);
errorEl.textContent = isValid ? '' : errorMessage;
errorEl.hidden = isValid;
});
input.addEventListener('input', () => {
// При вводе убираем ошибку (покажем снова при blur)
input.classList.remove('invalid', 'valid');
errorEl.hidden = true;
});
}
setupFieldValidation(
document.querySelector('#email'),
value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
'Введите корректный email'
);
Управление фокусом программно
// Установить фокус
const firstInput = document.querySelector('input:first-of-type');
firstInput.focus();
// Убрать фокус
input.blur();
// Проверить, есть ли фокус у элемента
if (document.activeElement === input) {
console.log('Инпут в фокусе');
}
// Показать элемент и поставить фокус (для модального окна)
function openModal(modal) {
modal.hidden = false;
const firstFocusable = modal.querySelector('button, input, a, [tabindex]');
firstFocusable?.focus();
}
Ловушка фокуса (Focus Trap) для модальных окон
function trapFocus(modal) {
const focusable = modal.querySelectorAll(
'button, input, select, textarea, a[href], [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
modal.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey) {
// Shift+Tab: если первый элемент — переходим к последнему
if (document.activeElement === first) {
e.preventDefault();
last.focus();
}
} else {
// Tab: если последний элемент — переходим к первому
if (document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
});
}
tabindex
// tabindex="0" — элемент участвует в Tab-навигации
// tabindex="-1" — фокус только программный (focus), не через Tab
// tabindex="1" и выше — явный порядок (не рекомендуется)
const div = document.querySelector('.card');
div.setAttribute('tabindex', '0'); // div теперь фокусируемый
div.focus();
Частые ошибки
1. Делегирование focus через addEventListener на родителе
// focus не всплывает — это не сработает
container.addEventListener('focus', handler); // не поймает фокус потомков
// Правильно: focusin
container.addEventListener('focusin', handler);
// Или: capture phase
container.addEventListener('focus', handler, true); // третий аргумент = capture
2. focus до добавления в DOM
const input = document.createElement('input');
input.focus(); // ничего не произойдёт — элемент не в DOM
document.body.appendChild(input);
input.focus(); // теперь работает
Связанные темы
- Работа с формами через JS
- События формы -- submit, input, change
- События клавиатуры -- keydown, keyup
- _MOC DOM
- _MOC JavaScript