Фокус и tabindex

В библиотеке Interact.js управление фокусом элементов и их взаимодействие с клавиатурой является важным аспектом для создания доступных и интерактивных интерфейсов. Встроенные возможности библиотеки позволяют отслеживать фокус, работать с навигацией по клавишам и управлять поведением элементов через атрибут tabindex.


Атрибут tabindex и его роль

Атрибут tabindex определяет порядок навигации по элементам с помощью клавиши Tab. Его значения могут быть следующими:

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

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


Отслеживание фокуса

Interact.js предоставляет возможность реагировать на события фокуса и потери фокуса с помощью стандартных событий DOM:

interact('.draggable')
  .on('focusin', event => {
    event.target.style.borderColor = 'blue';
  })
  .on('focusout', event => {
    event.target.style.borderColor = '';
  });
  • focusin – срабатывает при получении элемента фокуса.
  • focusout – срабатывает при потере фокуса.

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


Клавиатурное управление элементами

Для интерактивности элементов с клавиатуры используется сочетание событий keydown и методов Interact.js, таких как dragMoveListener.

Пример управления перетаскиванием с клавиатуры:

const draggableElement = document.querySelector('.draggable');

draggableElement.addEventListener('keydown', event => {
  const step = 10;
  let transform = draggableElement.style.transform || 'translate(0px, 0px)';
  const match = transform.match(/translate\(([-\d]+)px, ([-\d]+)px\)/);
  let x = 0, y = 0;
  if (match) {
    x = parseInt(match[1]);
    y = parseInt(match[2]);
  }

  switch(event.key) {
    case 'ArrowUp': y -= step; break;
    case 'ArrowDown': y += step; break;
    case 'ArrowLeft': x -= step; break;
    case 'ArrowRight': x += step; break;
  }

  draggableElement.style.transform = `translate(${x}px, ${y}px)`;
});

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


Управление фокусом через Interact.js

Interact.js поддерживает методы программного управления фокусом через DOM API:

  • element.focus() – переводит фокус на элемент.
  • element.blur() – снимает фокус с элемента.

Часто используется сочетание с событиями tap или drag для автоматического фокусирования элемента при взаимодействии:

interact('.draggable')
  .on('tap', event => {
    event.currentTarget.focus();
  });

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


Рекомендации по комбинации tabindex и интерактивности

  1. Естественный порядок табуляции (tabindex="0") – предпочтителен для большинства интерактивных элементов, чтобы сохранять стандартное поведение браузера.
  2. Исключение элементов из последовательности (tabindex="-1") – используется для вспомогательных или временно недоступных элементов.
  3. Положительные значения tabindex – применять осторожно, так как они могут нарушать естественный порядок навигации.
  4. Визуальная индикация фокуса – использовать стили для фокусированных элементов, чтобы пользователь видел, какой элемент активен.

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

interact('.draggable')
  .draggable({
    modifiers: [
      interact.modifiers.restrictRect({
        restriction: 'parent',
        endOnly: true
      })
    ],
    listeners: {
      move(event) {
        const target = event.target;
        const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
        const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

        target.style.transform = `translate(${x}px, ${y}px)`;
        target.setAttribute('data-x', x);
        target.setAttribute('data-y', y);
      }
    }
  })
  .on('focusin', event => event.target.style.outline = '2px solid #007bff')
  .on('focusout', event => event.target.style.outline = 'none');

В этом примере:

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

Интеграция с экранными читалками

Правильное использование tabindex совместно с aria-атрибутами обеспечивает поддержку экранных читалок. Для перетаскиваемых элементов рекомендуется:

  • Добавлять aria-grabbed="true/false" для отображения состояния.
  • Использовать role="button" или role="slider" в зависимости от контекста.
  • Обновлять aria-label при изменении состояния элемента.
draggableElement.setAttribute('aria-grabbed', 'true');
draggableElement.setAttribute('aria-label', 'Перетаскиваемый элемент, текущее положение x:10, y:20');

Это обеспечивает доступность, согласованную с практиками WAI-ARIA, и делает интерфейс Interact.js полностью функциональным для всех пользователей.