Поиск роли с учетом атрибутов

Библиотека Aria-query предоставляет структурированные данные о ARIA-ролях и атрибутах, которые помогают анализировать семантику элементов HTML и создавать доступные интерфейсы. Центральное понятие — объект roles, представляющий каждую ARIA-роль как набор свойств: поддерживаемые атрибуты, состояния и допустимые контейнеры.

Каждая роль описывается в виде:

{
  name: "button",
  abstract: false,
  superClass: ["command"],
  requiredProps: [],
  supportedProps: ["aria-pressed", "aria-expanded"],
  requiredContextRole: null,
  ownedBy: null
}
  • abstract — указывает, что роль абстрактная и не может использоваться напрямую в DOM.
  • superClass — список ролей-родителей, задающих общую семантику.
  • requiredProps — обязательные атрибуты для корректного использования роли.
  • supportedProps — дополнительные атрибуты, которые роль может использовать.
  • requiredContextRole — контекстная роль родителя, необходимая для корректного вложения.
  • ownedBy — роли элементов-контейнеров, которым данная роль принадлежит.

Получение списка ролей

Aria-query позволяет получить все известные роли через объект roles:

import { roles } from 'aria-query';

console.log([...roles.keys()]);

Это возвращает массив строк с именами всех ARIA-ролей, включая как abstract, так и конкретные роли.

Поиск ролей по поддерживаемым атрибутам

Для поиска ролей, которые поддерживают определённый ARIA-атрибут, используется фильтрация roles:

const attribute = 'aria-expanded';
const rolesWithAttr = [...roles.entries()]
  .filter(([roleName, roleData]) => roleData.supportedProps.includes(attribute))
  .map(([roleName]) => roleName);

console.log(rolesWithAttr);

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

  • supportedProps содержит все атрибуты, разрешённые для роли.
  • Фильтрация позволяет определить, какие роли поддерживают интерактивное поведение, например, раскрытие (aria-expanded) или состояние выбора (aria-checked).

Учёт контекста роли

Некоторые роли требуют, чтобы их родитель имел определённую роль или был контейнером конкретного типа. Это задаётся через requiredContextRole и ownedBy.

Пример поиска ролей, которые могут находиться внутри списка (list):

const parentRole = 'list';
const childRoles = [...roles.entries()]
  .filter(([roleName, roleData]) => roleData.requiredContextRole === parentRole || (roleData.ownedBy && roleData.ownedBy.includes(parentRole)))
  .map(([roleName]) => roleName);

console.log(childRoles);
  • requiredContextRole гарантирует, что элемент будет валиден только внутри определённого родителя.
  • ownedBy указывает на роли контейнеров, которые могут владеть данной ролью.
  • Этот подход важен для построения иерархии элементов интерфейса с правильной семантикой.

Работа с абстрактными и конкретными ролями

Абстрактные роли (abstract: true) не используются напрямую в DOM, но служат для классификации и наследования.

Для фильтрации конкретных ролей используется:

const concreteRoles = [...roles.entries()]
  .filter(([_, roleData]) => !roleData.abstract)
  .map(([roleName]) => roleName);

console.log(concreteRoles);
  • Это важно при генерации списка доступных для использования ролей в интерфейсе.
  • Абстрактные роли, такие как command или widget, служат только для объединения семантики нескольких конкретных ролей.

Примеры комплексного поиска

Поиск интерактивных ролей с aria-checked:

const interactiveRoles = [...roles.entries()]
  .filter(([_, roleData]) => roleData.supportedProps.includes('aria-checked') && !roleData.abstract)
  .map(([roleName]) => roleName);

console.log(interactiveRoles); // ["checkbox", "menuitemcheckbox", "radio"]

Поиск ролей, которые можно использовать внутри table:

const tableChildRoles = [...roles.entries()]
  .filter(([_, roleData]) => roleData.requiredContextRole === 'table' || (roleData.ownedBy && roleData.ownedBy.includes('table')))
  .map(([roleName]) => roleName);

console.log(tableChildRoles); // ["row", "rowgroup", "cell", ...]

Выводы по фильтрации

  • Фильтрация по supportedProps позволяет находить роли по атрибутам.
  • Учет requiredContextRole и ownedBy обеспечивает правильную вложенность элементов.
  • Исключение абстрактных ролей помогает сосредоточиться на реально используемых элементах.

Такой подход позволяет создавать динамические проверки доступности интерфейсов, формировать рекомендации по ролям для компонентов и обеспечивать соответствие спецификации ARIA.