Управление фокусом

Stimulus — это минималистичный JavaScript-фреймворк, предназначенный для добавления динамического поведения к HTML-элементам без создания сложной архитектуры SPA. Одной из ключевых задач при разработке интерактивных интерфейсов является управление фокусом элементов. Правильная работа с фокусом улучшает пользовательский опыт, повышает доступность и обеспечивает корректное взаимодействие с формами, модальными окнами и динамическими компонентами.

Контроллеры и привязка действий

В Stimulus управление фокусом осуществляется через контроллеры, которые связывают HTML-элементы с методами JavaScript. Контроллеры создаются с помощью класса Controller и подключаются к элементам через атрибут data-controller.

<div data-controller="focus">
  <input data-focus-target="input" type="text">
  <button data-action="click->focus#setFocus">Установить фокус</button>
</div>
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["input"];

  setFocus() {
    this.inputTarget.focus();
  }
}

Ключевые моменты:

  • data-focus-target указывает, на какой элемент контроллер может воздействовать.
  • data-action связывает событие с методом контроллера (click->focus#setFocus).

Управление фокусом при динамическом добавлении элементов

В динамических интерфейсах элементы могут появляться после загрузки страницы. Для установки фокуса на только что добавленные элементы используется метод connect контроллера.

connect() {
  if (this.hasInputTarget) {
    this.inputTarget.focus();
  }
}

Этот подход позволяет гарантировать, что пользовательский фокус всегда окажется на нужном элементе, независимо от того, когда элемент добавлен в DOM.

Работа с несколькими целевыми элементами

Stimulus поддерживает массивы целей, что удобно для форм с множественными полями. Пример:

static targets = ["input"];

focusFirst() {
  if (this.inputTargets.length > 0) {
    this.inputTargets[0].focus();
  }
}

focusLast() {
  if (this.inputTargets.length > 0) {
    this.inputTargets[this.inputTargets.length - 1].focus();
  }
}

В данном случае inputTargets возвращает массив всех элементов с target=“input”, что позволяет управлять порядком фокуса программно.

Перехват и перенаправление фокуса

Для улучшения навигации по форме или модальному окну можно использовать события focus и blur. Stimulus позволяет легко обрабатывать их через data-action.

<input data-action="focus->focus#onFocus blur->focus#onBlur">
onFocus(event) {
  console.log("Элемент получил фокус:", event.target);
}

onBlur(event) {
  console.log("Элемент потерял фокус:", event.target);
}

Это особенно полезно для валидации форм в реальном времени или для управления стилями активных полей.

Управление фокусом в модальных и диалоговых окнах

В интерактивных интерфейсах часто требуется ограничивать фокус только элементами модального окна (focus trap). В Stimulus это реализуется путем установки фокуса на первый интерактивный элемент при открытии и обработки клавиши Tab.

trapFocus(event) {
  const focusableElements = this.element.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])");
  const firstElement = focusableElements[0];
  const lastElement = focusableElements[focusableElements.length - 1];

  if (event.key === "Tab") {
    if (event.shiftKey && document.activeElement === firstElement) {
      event.preventDefault();
      lastElement.focus();
    } else if (!event.shiftKey && document.activeElement === lastElement) {
      event.preventDefault();
      firstElement.focus();
    }
  }
}

Метод trapFocus можно привязать к событию keydown всего модального окна. Такой подход обеспечивает циклическое перемещение фокуса внутри модального окна, предотвращая случайный выход за пределы диалога.

Анимации и задержка установки фокуса

Если элемент появляется с анимацией, установка фокуса сразу после добавления может не сработать. В таких случаях применяется задержка через setTimeout или слушатель события transitionend.

showElement() {
  this.element.classList.add("visible");
  this.element.addEventListener("transitionend", () => {
    this.inputTarget.focus();
  }, { once: true });
}

Это гарантирует, что фокус будет установлен только после завершения анимации появления.

Итоговая структура контроллера

Эффективный контроллер управления фокусом может содержать следующие компоненты:

  • targets — для указания элементов, на которые можно воздействовать.
  • actions — для привязки событий click, focus, blur и клавиатурных событий.
  • connect — для установки фокуса при инициализации.
  • методы управления фокусомsetFocus, focusFirst, focusLast, trapFocus.

Такой подход обеспечивает гибкое, модульное и легко расширяемое управление фокусом, что критично для интерактивных веб-приложений.