Переключение между панелями

Библиотека Tabbable предназначена для определения всех элементов на веб-странице, которые могут быть сфокусированы с помощью клавиши Tab. Она не изменяет поведение браузера, а лишь предоставляет удобный способ работы с фокусируемыми элементами. В контексте переключения между панелями это особенно важно, так как позволяет программно управлять навигацией без вмешательства в стандартное поведение клавиатуры.

Основные функции

  • tabbable(container): возвращает массив всех фокусируемых элементов внутри указанного контейнера.
  • isTabbable(element): проверяет, можно ли элемент фокусировать через Tab.
  • focusable(element): возвращает true, если элемент может получить фокус программно (не обязательно через Tab).

Пример базового использования:

import { tabbable } from 'tabbable';

const panel = document.querySelector('#panel-1');
const focusableElements = tabbable(panel);
console.log(focusableElements);

Этот код вернёт массив всех элементов, на которые пользователь может переключиться с помощью клавиши Tab внутри панели #panel-1.


Переключение фокуса между панелями

В интерфейсах с несколькими панелями важно управлять порядком фокусируемых элементов. Например, при закрытии одной панели фокус нужно переносить на следующую или предыдущую панель, чтобы пользователь не терял ориентир.

Получение фокусируемых элементов

Для каждой панели можно создать отдельный список элементов:

const panels = document.querySelectorAll('.panel');

const panelTabbables = Array.from(panels).map(panel => ({
  panel,
  elements: tabbable(panel)
}));

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

  • Циклическое переключение между элементами внутри панели.
  • Перенос фокуса на следующую панель при достижении конца текущей.

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

Чтобы установить фокус на первый элемент панели:

function focusFirst(panel) {
  const elements = tabbable(panel);
  if (elements.length > 0) {
    elements[0].focus();
  }
}

Для фокусировки последнего элемента:

function focusLast(panel) {
  const elements = tabbable(panel);
  if (elements.length > 0) {
    elements[elements.length - 1].focus();
  }
}

Эти функции позволяют реализовать логику перехода между панелями при нажатии клавиш Tab и Shift+Tab.


Обработка клавиатурных событий

Для полноценного переключения между панелями требуется слушать события клавиатуры и корректно определять направление навигации:

document.addEventListener('keydown', (event) => {
  if (event.key !== 'Tab') return;

  const activeElement = document.activeElement;
  const currentPanelIndex = Array.from(panels).findIndex(panel =>
    panel.contains(activeElement)
  );
  const currentPanelElements = panelTabbables[currentPanelIndex].elements;

  if (!event.shiftKey && activeElement === currentPanelElements[currentPanelElements.length - 1]) {
    event.preventDefault();
    const nextIndex = (currentPanelIndex + 1) % panels.length;
    focusFirst(panels[nextIndex]);
  }

  if (event.shiftKey && activeElement === currentPanelElements[0]) {
    event.preventDefault();
    const prevIndex = (currentPanelIndex - 1 + panels.length) % panels.length;
    focusLast(panels[prevIndex]);
  }
});

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

  • Используется event.preventDefault() для предотвращения стандартного поведения браузера.
  • Логика учитывает цикличность: при достижении конца последней панели фокус возвращается на первую и наоборот.
  • С shiftKey реализуется обратное переключение.

Динамические панели

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

function updatePanels() {
  const panels = document.querySelectorAll('.panel');
  panelTabbables.length = 0;
  panels.forEach(panel => {
    panelTabbables.push({
      panel,
      elements: tabbable(panel)
    });
  });
}

Это важно для интерфейсов с вкладками, модальными окнами и панелями с подгружаемым контентом.


Настройка фильтров и исключений

Tabbable позволяет фильтровать элементы, которые не должны участвовать в цикле фокуса. Например, можно исключить скрытые элементы или те, которые временно недоступны:

import { tabbable } from 'tabbable';

const focusableVisible = tabbable(panel, { includeContainer: false }).filter(el => el.offsetParent !== null);

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


Использование с компонентами UI

В современных фреймворках (React, Vue, Svelte) Tabbable особенно полезна для:

  • Переключения между вкладками.
  • Управления фокусом в модальных окнах.
  • Реализации кастомных списков и аккордеонов.

Преимущество в том, что библиотека не привязывается к конкретному фреймворку и работает напрямую с DOM, что делает её гибкой для интеграции в сложные интерфейсы.


Практические рекомендации

  • Всегда получать фокусируемые элементы актуально перед переключением, особенно если DOM динамичен.
  • Использовать циклическую навигацию внутри панелей для улучшения UX.
  • Обрабатывать Shift+Tab отдельно, чтобы направление навигации не ломалось.
  • Фильтровать элементы с display: none и disabled, чтобы исключить нежелательные объекты из цикла.

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