Avatar ·

Как определить, какой DOM-элемент находится в фокусе на веб-странице?

Всем привет! Я делаю небольшой текстовый редактор на чистом JavaScript, и мне нужно отслеживать, в каком именно поле ввода пользователь сейчас печатает. У меня есть несколько ` ``` ```javascript // Я хочу получить элемент, который сейчас в фокусе let focusedElement = document.activeElement; console.log(focusedElement); // Выводит body или undefined? ``` Когда я кликаю на поле ввода и вызываю эту функцию, в консоли часто показывается `` или `null`. Почему так происходит? Как правильно получить именно тот элемент, который я выбрал? И есть ли способ отслеживать изменения фокуса в реальном времени, чтобы, например, менять стили при переходе между полями? Буду благодарен за подробное объяснение с примерами. Заранее спасибо! <<>>

Основное решение: свойство document.activeElement

Для определения текущего сфокусированного DOM-элемента используется свойство document.activeElement. Оно возвращает ссылку на элемент, который в данный момент находится в фокусе (например, <input>, <textarea>, <button>, <a> с tabindex, <select> и т.д.). Если ни один элемент не имеет фокуса, возвращается <body> или null.

В вашем коде проблема в том, что вы вызываете document.activeElement сразу после загрузки страницы, когда фокус ещё не установлен ни на один из элементов. В этот момент активным элементом является <body> (или <html>). Чтобы получить корректный результат, нужно вызывать проверку после того, как пользователь кликнул на элемент или перешёл к нему с помощью Tab.

Правильный пример использования

// HTML:
// <input type="text" id="name" placeholder="Введите имя">
// <textarea id="message" placeholder="Ваше сообщение"></textarea>
// <button id="btn">Отправить</button>

// JavaScript:
document.getElementById('name').addEventListener('focus', function() {
  console.log('Текущий фокус:', document.activeElement); // <input id="name">
});

// Или универсальный обработчик на все фокусируемые элементы:
document.addEventListener('focusin', function() {
  let focused = document.activeElement;
  console.log('Активный элемент:', focused.tagName, focused.id || '(без id)');
});

Как отслеживать изменения фокуса в реальном времени

Для этого используйте события focusin и focusout (или focus и blur). focusin всплывает, поэтому его можно повесить на родительский контейнер или на document:

document.addEventListener('focusin', function(event) {
  // event.target — это элемент, который получил фокус
  let target = event.target;
  console.log('Фокус на:', target);
  
  // Пример: подсветка активного поля
  target.style.outline = '2px solid blue';
});

document.addEventListener('focusout', function(event) {
  // event.target — элемент, который потерял фокус
  let target = event.target;
  target.style.outline = ''; // убираем подсветку
});

Важные нюансы

  • Работа во всех браузерах: document.activeElement поддерживается всеми современными браузерами, включая IE9+. Для старых IE (8 и ниже) может возвращать null вместо <body>.
  • Элементы, которые могут получить фокус: по умолчанию это <input>, <textarea>, <select>, <button>, <a href>, а также элементы с атрибутом tabindex (например, <div tabindex="0">).
  • Проверка на null: в некоторых случаях (например, при скрытии элемента) document.activeElement может вернуть null. Всегда проверяйте: if (document.activeElement) { ... }.
  • Разница между focus и focusin: focus не всплывает, поэтому его нужно вешать на каждый элемент отдельно. focusin всплывает, что удобно для делегирования.

Пример: получение id или класса активного элемента

function getActiveElementInfo() {
  const active = document.activeElement;
  if (!active || active === document.body) {
    return 'Фокус не на интерактивном элементе';
  }
  return {
    tag: active.tagName,
    id: active.id,
    class: active.className,
    type: active.type || '—'
  };
}

// Используйте в любом обработчике
document.addEventListener('click', function() {
  console.log(getActiveElementInfo());
});

Таким образом, document.activeElement — это ваш главный инструмент. А в паре с событиями focusin/focusout вы сможете легко отслеживать и реагировать на изменения фокуса.

<<>>

Альтернативный подход: использование querySelector с :focus

В CSS существует псевдокласс :focus, который можно применить и в JavaScript через метод querySelector. Это может быть удобно, когда нужно найти единственный сфокусированный элемент в определённом контейнере или если вы работаете с Shadow DOM.

// Найти сфокусированный элемент во всём документе
let focusedElement = document.querySelector(':focus');
console.log(focusedElement);

// Найти сфокусированный элемент внутри конкретного контейнера
let container = document.getElementById('myForm');
let focusedInside = container.querySelector(':focus');
console.log(focusedInside); // null, если фокус вне контейнера

Преимущества:

  • Работает даже в старых браузерах (IE8+), если использовать полифиллы для querySelector.
  • Позволяет легко проверить фокус внутри конкретной области, а не во всём документе.
  • Может быть полезен при работе с теневой DOM (Shadow DOM), где document.activeElement не всегда видит элементы внутри теневого дерева.

Недостатки:

  • Возвращает null, если ни один элемент не находится в фокусе, в отличие от document.activeElement, который возвращает <body>. Это может быть как плюсом, так и минусом в зависимости от задачи.
  • Медленнее, чем прямое обращение к document.activeElement, так как выполняет поиск по DOM-дереву.

В большинстве случаев document.activeElement предпочтительнее, но querySelector(':focus') — хорошая альтернатива для специфических сценариев.

// Пример: подсветка только если фокус внутри формы
if (document.querySelector('#myForm :focus')) {
  console.log('Фокус внутри формы');
} else {
  console.log('Фокус вне формы');
}