Focus management

Фокусировка элементов — ключевой аспект доступности и удобства взаимодействия с интерфейсом. В современных UI-библиотеках на SvelteKit управление фокусом реализуется через сочетание реактивности Svelte и специализированных утилит для контроля активного элемента.


Основные принципы фокус-менеджмента

  1. Контроль активного элемента Каждый интерактивный элемент (кнопки, ссылки, поля ввода) должен корректно получать и терять фокус. В SvelteKit это достигается через директивы bind:this для получения ссылки на DOM-узел и методы .focus() / .blur().

    <input bind:this={inputEl} placeholder="Введите текст" />
    <button on:click={() => inputEl.focus()}>Фокус на поле</button>
    
    <script>
      let inputEl;
    </script>
  2. Табуляция и порядок навигации Важным аспектом является логика навигации с клавиатуры. Атрибут tabindex управляет порядком обхода элементов:

    • tabindex="0" — элемент участвует в естественной последовательности табуляции.
    • tabindex="-1" — элемент не доступен для табуляции, но доступен программно через .focus().

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

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

  • Автофокус на открытии После монтирования модального окна первый интерактивный элемент должен получить фокус. В SvelteKit это удобно делать через lifecycle hook onMount:

    <script>
      import { onMount } from 'svelte';
      let firstInput;
    
      onMount(() => {
        firstInput.focus();
      });
    </script>
    
    <input bind:this={firstInput} placeholder="Имя" />
  • Фокус-трап (focus trap) Чтобы предотвратить уход фокуса за пределы модального окна при использовании клавиши Tab, применяются циклические обходы элементов. В SvelteKit можно реализовать через массив интерактивных элементов и события keydown:

    <script>
      import { onMount } from 'svelte';
      let modalEl;
      let focusableEls = [];
    
      onMount(() => {
        focusableEls = modalEl.querySelectorAll('button, [tabindex="0"], input, select, textarea');
      });
    
      function trapFocus(event) {
        const firstEl = focusableEls[0];
        const lastEl = focusableEls[focusableEls.length - 1];
    
        if (event.key === 'Tab') {
          if (event.shiftKey && document.activeElement === firstEl) {
            event.preventDefault();
            lastEl.focus();
          } else if (!event.shiftKey && document.activeElement === lastEl) {
            event.preventDefault();
            firstEl.focus();
          }
        }
      }
    </script>
    
    <div bind:this={modalEl} on:keydown={trapFocus} tabindex="-1">
      <input placeholder="Имя" />
      <button>Сохранить</button>
    </div>

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

При динамическом добавлении или удалении элементов важно сохранять предсказуемое поведение фокуса:

  • Добавление элемента — фокус может автоматически переходить на новый элемент:

    <script>
      let items = ['Элемент 1', 'Элемент 2'];
    
      function addItem() {
        items = [...items, `Элемент ${items.length + 1}`];
        // задержка для рендера
        tick().then(() => {
          document.querySelectorAll('li')[items.length - 1].focus();
        });
      }
      import { tick } from 'svelte';
    </script>
    
    <ul>
      {#each items as item}
        <li tabindex="0">{item}</li>
      {/each}
    </ul>
    <button on:click={addItem}>Добавить элемент</button>
  • Удаление элемента — фокус следует возвращать на ближайший предыдущий элемент или на родительский контейнер.


Интеграция с SvelteKit UI-библиотеками

Многие UI-библиотеки для SvelteKit предоставляют готовые утилиты для фокус-менеджмента:

  • shadcn/ui — компоненты Dialog, Popover, Tabs имеют встроенный фокус-трап и автопозиционирование фокуса.
  • svelte-headlessui — реализует управление фокусом через ARIA-атрибуты и lifecycle-хуки, включая обработку клавиатурной навигации.

Ключевые моменты при использовании таких библиотек:

  • Не использовать autofocus на нескольких элементах одновременно.
  • При кастомизации компонентов сохранять логику фокус-трапа.
  • Проверять поведение на клавиатурной навигации и с экранными ридерами для обеспечения доступности.

Контроль фокуса через кастомные actions

Svelte предоставляет механизм actions, позволяющий оборачивать логику фокуса в переиспользуемый модуль:

<script>
  export function autoFocus(node) {
    node.focus();
    return {
      destroy() {}
    };
  }
</script>

<input use:autoFocus placeholder="Фокус автоматически" />

Этот подход позволяет централизованно управлять поведением фокуса и легко интегрировать его с UI-компонентами.


Важные практики

  • Всегда проверять, куда уходит фокус при закрытии модальных элементов.
  • Поддерживать последовательность табуляции, соответствующую визуальному порядку элементов.
  • Использовать aria-* атрибуты (aria-modal, aria-labelledby) совместно с фокус-менеджментом для доступности.
  • Избегать потери фокуса при перерендеринге компонентов через реактивные переменные и keyed each blocks.

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