Особенности работы с SVG

Библиотека Tabbable предназначена для определения фокусируемых элементов на странице и управления последовательностью их навигации с помощью клавиши Tab. При работе с SVG-элементами необходимо учитывать специфику DOM SVG, отличающуюся от HTML. SVG содержит графические элементы, такие как <circle>, <rect>, <path>, <text> и <g>, которые по умолчанию не являются фокусируемыми. Это влияет на поведение Tabbable, так как библиотека ориентируется на стандартные HTML-элементы, поддерживающие свойства tabindex и focusable.


Фокусируемость SVG-элементов

  1. Атрибут focusable В SVG версии 1.1 и выше существует атрибут focusable, который определяет, может ли элемент принимать фокус. Его значения:

    • true — элемент может быть сфокусирован.
    • false — элемент игнорируется при навигации.
    • Отсутствие атрибута — поведение зависит от браузера (в большинстве случаев элемент не фокусируемый).

    Пример:

    <svg width="200" height="100">
      <rect x="10" y="10" width="100" height="50" fill="blue" focusable="true" tabindex="0"></rect>
    </svg>

    Здесь <rect> становится доступным для Tabbable и может быть частью таб-цепочки.

  2. Атрибут tabindex Для интеграции SVG в последовательность табуляции необходимо явно указывать tabindex. Значения:

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

    Комбинация focusable="true" и tabindex="0" делает SVG-элемент полностью совместимым с Tabbable.


Использование Tabbable с SVG

Библиотека предоставляет методы tabbable() и isTabbable() для работы с DOM-элементами. Для SVG их применение требует учета фокусируемости:

import { tabbable } from 'tabbable';

const svgElement = document.querySelector('svg');
const focusableElements = tabbable(svgElement);

console.log(focusableElements);

Особенности:

  • Tabbable игнорирует элементы без tabindex или focusable="true".
  • Для <g> (группировка) Tabbable не будет автоматически искать вложенные элементы, если сама группа не фокусируемая. Необходимо либо добавить tabindex к <g>, либо обрабатывать дочерние элементы отдельно.
  • Свойство disabled для <button> в SVG-аналогах (например, <foreignObject> с HTML-кнопкой) корректно учитывается Tabbable.

Обработка событий фокуса

SVG-элементы с tabindex поддерживают события focus и blur. Это важно для создания интерактивных графиков, карт и кнопок внутри SVG. Пример:

const rect = document.querySelector('rect');

rect.addEventListener('focus', () => {
  rect.setAttribute('fill', 'red'); // визуальная подсветка
});

rect.addEventListener('blur', () => {
  rect.setAttribute('fill', 'blue');
});

Tебательно, что без tabindex эти события не сработают.


Особенности взаимодействия с Shadow DOM и SVG

Если SVG находится внутри Shadow DOM:

  • Tabbable корректно находит элементы внутри open shadow root, но элементы в closed shadow root будут недоступны.
  • Атрибуты tabindex и focusable должны быть явно указаны для каждого SVG-элемента.
  • Для сложных интерфейсов часто используют комбинацию tabbable() и ручного обхода querySelectorAll для SVG-групп.

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

  1. Для всех интерактивных SVG-элементов устанавливать focusable="true" и tabindex="0".
  2. Группирующие <g> использовать с осторожностью — лучше фокусировать дочерние элементы.
  3. Для динамически создаваемых SVG-элементов вызывать tabbable() после добавления в DOM, иначе новые элементы не будут учтены.
  4. Визуальные индикаторы фокуса должны быть реализованы через SVG-атрибуты (stroke, fill, opacity) или CSS внутри <foreignObject>.

Ограничения

  • Не все браузеры одинаково обрабатывают focusable для SVG 1.1 и SVG 2.0, особенно в Edge Legacy и IE11.
  • Tabbable не умеет работать с clipPath и другими декоративными элементами — они никогда не фокусируются.
  • Элементы <text> могут вести себя непредсказуемо без tabindex.

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

При использовании UI-фреймворков (React, Vue, Svelte) SVG-фокусируемые элементы:

  • В React важно добавлять tabIndex (с большой буквы), иначе JSX не интерпретирует атрибут корректно.
  • Для анимаций фокуса рекомендуется использовать CSS-классы с селекторами :focus или слушать события focus/blur для управления стилями SVG.

Эти подходы обеспечивают корректное взаимодействие Tabbable с SVG и позволяют строить полностью клавиатурно-доступные графические интерфейсы.