Tabbable — это небольшая, но мощная библиотека для JavaScript, предназначенная для работы с интерактивными элементами страницы, которые могут быть достигнуты с помощью клавиши Tab. Она позволяет точно определять, какие элементы доступны для фокусировки, что особенно важно при создании доступных интерфейсов и сложных компонентов пользовательского интерфейса.
Для использования Tabbable в проекте на JavaScript или Svelte необходимо установить пакет через npm:
npm install tabbable
Импортируется библиотека следующим образом:
import { tabbable } from 'tabbable';
В Svelte можно использовать этот импорт прямо внутри скрипта компонента:
<script>
import { tabbable } from 'tabbable';
</script>
Метод tabbable(container, options) возвращает массив
элементов, которые могут получить фокус с помощью клавиши
Tab.
Сигнатура метода:
tabbable(container: HTMLElement, options?: { includeContainer?: boolean, strategy?: 'strict' | 'all' }): HTMLElement[]
Параметры:
container — родительский элемент, внутри которого
производится поиск tabbable-элементов.
options.includeContainer — если true,
сам контейнер также проверяется на возможность получения
фокуса.
options.strategy — стратегия поиска:
'strict' — только элементы, полностью соответствующие
стандарту фокусируемых элементов.'all' — включает элементы, которые могут быть
фокусируемыми через программный фокус, но не обязательно через Tab.Возвращаемое значение: массив элементов типа
HTMLElement.
Элементы считаются tabbable, если они соответствуют следующим условиям:
display не
none, visibility не hidden,
opacity не 0 при родительских ограничениях).disabled !== true).tabindex ≥ 0, элементы <a> с
href, <button>,
<input>, <textarea>,
<select>).overflow: hidden или
clip-path, которые могут ограничивать видимость
элемента.Пример использования:
const container = document.querySelector('#form');
const focusableElements = tabbable(container);
focusableElements.forEach(el => {
console.log(el.tagName, el.tabIndex);
});
Svelte actions предоставляют удобный способ связывать поведение DOM-элементов с жизненным циклом компонента. Tabbable можно использовать через action для автоматического получения или управления tabbable-элементами.
Пример action для выделения первого tabbable-элемента внутри контейнера:
<script>
import { tabbable } from 'tabbable';
function focusFirstTabbable(node) {
const elements = tabbable(node);
if (elements.length > 0) {
elements[0].focus();
}
return {
destroy() {
// очистка при удалении элемента, если необходимо
}
};
}
</script>
<div use:focusFirstTabbable>
<input type="text" />
<button>Отправить</button>
<a href="#">Ссылка</a>
</div>
В этом примере:
use:focusFirstTabbable — Svelte action, которая
активируется при монтировании элемента.tabbable(node) и устанавливает фокус на первый.Tabbable учитывает значение атрибута
tabindex, что позволяет контролировать порядок
фокусировки:
tabindex="0" — элемент включается в естественный
порядок табуляции.tabindex="-1" — элемент может быть сфокусирован
программно, но пропускается при Tab.tabindex — задают явный порядок
табуляции, приоритет перед 0.Пример динамического управления tabindex:
<script>
import { tabbable } from 'tabbable';
let container;
function disableTabbable() {
tabbable(container).forEach(el => el.tabIndex = -1);
}
function enableTabbable() {
tabbable(container).forEach(el => el.tabIndex = 0);
}
</script>
<div bind:this={container}>
<input type="text" />
<button>Кнопка</button>
</div>
<button on:click={disableTabbable}>Отключить Tab</button>
<button on:click={enableTabbable}>Включить Tab</button>
Tabbable предоставляет гибкие стратегии для поиска:
strict — учитывает только реально доступные для Tab
элементы. Используется для реализации навигации по клавиатуре.all — включает элементы, которые можно программно
сфокусировать. Полезно для случаев, когда требуется установить фокус
через JavaScript без участия Tab.Пример:
const allFocusable = tabbable(container, { strategy: 'all' });
В Svelte-компонентах с модальными окнами, табами или аккордеонами Tabbable позволяет:
focus trap).Пример реализации фокус-трапа в Svelte:
<script>
import { tabbable } from 'tabbable';
let modal;
function trapFocus(node) {
const elements = tabbable(node);
let first = elements[0];
let last = elements[elements.length - 1];
function handleKey(event) {
if (event.key === 'Tab') {
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
}
}
node.addEventListener('keydown', handleKey);
return {
destroy() {
node.removeEventListener('keydown', handleKey);
}
};
}
</script>
<div bind:this={modal} use:trapFocus>
<input type="text" />
<button>Закрыть</button>
</div>
В этом коде:
При изменении DOM (добавлении или удалении элементов) важно повторно
вычислять tabbable-элементы, так как массив, возвращаемый функцией
tabbable, не обновляется автоматически. В Svelte это удобно
реализовать через реактивные выражения:
<script>
import { tabbable } from 'tabbable';
let container;
$: focusable = tabbable(container);
</script>
<div bind:this={container}>
{#each items as item}
<button>{item}</button>
{/each}
</div>
Каждое изменение массива items автоматически обновит
список tabbable-элементов.
strategy: 'strict' снижает количество
проверок, ускоряя вычисление tabbable-элементов.Эта библиотека является базовым инструментом для контроля фокусировки в интерактивных компонентах, обеспечивая доступность и предсказуемое поведение клавиатурной навигации в приложениях на Svelte и чистом JavaScript.