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

В FAST Element управление фокусом строится на работе с нативными методами DOM и специальных API, предоставляемых библиотекой для упрощения взаимодействия с компонентами. Ключевым элементом является возможность контролировать фокус программно, реагировать на события фокуса и создавать доступные интерфейсы.


Атрибуты и свойства, влияющие на фокус

tabindex — основной инструмент управления порядком фокуса. В FAST Element этот атрибут можно динамически связывать с состоянием компонента:

import { FASTElement, html, css, attr } from "@microsoft/fast-element";

export class FocusExample extends FASTElement {
    @attr tabindexValue = 0;
}

FocusExample.define({
    name: "focus-example",
    template: html`
        <button tabindex="${x => x.tabindexValue}">Нажми меня</button>
    `,
    styles: css`button { padding: 8px; }`
});

Значения tabindex:

  • 0 — элемент доступен для фокусировки через клавишу Tab по естественному порядку.
  • -1 — элемент не участвует в последовательности фокуса, но может быть сфокусирован программно.
  • Положительное число — задаёт конкретный порядок обхода Tab.

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

FAST Element позволяет программно устанавливать фокус на элемент с помощью стандартного DOM API focus() или через привязку ссылок на элементы через директиву ref:

import { FASTElement, html, ref } from "@microsoft/fast-element";

export class FocusController extends FASTElement {
    inputElement;

    focusInput() {
        this.inputElement.focus();
    }
}

FocusController.define({
    name: "focus-controller",
    template: html`
        <input ${ref("inputElement")} placeholder="Введите текст" />
        <button @click="${x => x.focusInput()}">Фокус на input</button>
    `
});

Использование ref позволяет сохранить ссылку на внутренний элемент DOM и безопасно управлять его состоянием вне жизненного цикла шаблона.


События фокуса

FAST Element предоставляет стандартные события focus и blur, которые можно обрабатывать через шаблон или программно:

html`
    <input @focus="${x => x.onFocus()}" @blur="${x => x.onBlur()}" />
`

onFocus() {
    console.log("Элемент получил фокус");
}

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

Для комплексных компонентов важно учитывать делегирование событий фокуса. Например, если компонент содержит несколько вложенных элементов, можно использовать focusin и focusout, которые всплывают через дерево компонентов, в отличие от focus и blur.


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

Для создания доступных компонентов необходимо:

  1. Следить за логикой табуляции с помощью tabindex.
  2. Использовать ARIA-атрибуты (aria-label, aria-labelledby, aria-describedby) для элементов управления.
  3. Обрабатывать состояния фокуса визуально с помощью CSS:
:host(:focus-within) {
    outline: 2px solid var(--accent-color);
    border-radius: 4px;
}

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


Фокус и жизненный цикл компонента

FAST Element предоставляет методы жизненного цикла, которые полезны для корректного управления фокусом:

  • connectedCallback() — можно программно установить фокус на элемент после его добавления в DOM.
  • disconnectedCallback() — рекомендуется очищать ссылки на элементы, чтобы избежать утечек памяти при удалении компонента.

Пример:

connectedCallback() {
    super.connectedCallback();
    if (this.autoFocus) {
        this.shadowRoot.querySelector("input")?.focus();
    }
}

Вспомогательные методы для комплексных компонентов

Для компонентов с динамическими элементами или списками FAST Element позволяет создавать утилиты для управления фокусом:

  • Фокусировка первого доступного элемента:
focusFirst() {
    const first = this.shadowRoot.querySelector('[tabindex]:not([disabled])');
    first?.focus();
}
  • Фокусировка последнего элемента:
focusLast() {
    const elements = Array.from(this.shadowRoot.querySelectorAll('[tabindex]:not([disabled])'));
    elements[elements.length - 1]?.focus();
}
  • Циклический фокус при клавишах Tab и Shift+Tab:
handleKeyDown(event) {
    if (event.key === "Tab") {
        const focusable = Array.from(this.shadowRoot.querySelectorAll('[tabindex]:not([disabled])'));
        const currentIndex = focusable.indexOf(this.shadowRoot.activeElement);
        let nextIndex = event.shiftKey ? currentIndex - 1 : currentIndex + 1;
        if (nextIndex >= focusable.length) nextIndex = 0;
        if (nextIndex < 0) nextIndex = focusable.length - 1;
        focusable[nextIndex]?.focus();
        event.preventDefault();
    }
}

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


Интеграция с внешними библиотеками и роутерами

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

  • Сохранять состояние последнего сфокусированного элемента при навигации.
  • Восстанавливать фокус после обновления DOM через requestAnimationFrame или setTimeout для корректного рендеринга.

Пример восстановления фокуса:

restoreFocus() {
    requestAnimationFrame(() => {
        this.lastFocused?.focus();
    });
}

Управление фокусом в FAST Element позволяет создавать сложные, доступные и интерактивные компоненты, полностью контролируя поведение клавиатурного взаимодействия и визуальную подсветку состояния фокуса. Оптимальное использование tabindex, событий фокуса и методов жизненного цикла обеспечивает надёжное и предсказуемое поведение интерфейсов.