Accessibility и события

Lit — современный фреймворк для создания веб-компонентов на JavaScript, предоставляющий удобные механизмы рендеринга и реактивности. Одним из ключевых аспектов разработки компонентов является обеспечение доступности (accessibility, a11y) и корректная работа с событиями.

Обеспечение доступности

Веб-доступность включает поддержку клавиатурной навигации, корректную работу с экранными считывателями и семантическую разметку. Lit позволяет создавать компоненты, соблюдающие эти требования, за счёт гибкой работы с атрибутами и свойствами.

Использование семантических элементов и ARIA-атрибутов

При создании пользовательских компонентов важно использовать правильные HTML-теги и ARIA-атрибуты для передачи состояния и роли элементов:

import { LitElement, html, css } from 'lit';

class AccessibleButton extends LitElement {
  static properties = {
    disabled: { type: Boolean }
  };

  static styles = css`
    button:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }
  `;

  render() {
    return html`
      <button
        ?disabled=${this.disabled}
        aria-disabled=${this.disabled ? 'true' : 'false'}
        role="button"
      >
        Нажми меня
      </button>
    `;
  }
}

customElements.define('accessible-button', AccessibleButton);

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

  • Атрибут ?disabled=${this.disabled} синхронизирует состояние кнопки с DOM.
  • aria-disabled передаёт состояние для скринридеров.
  • role="button" уточняет семантику элемента, если она отличается от стандартной кнопки.

Клавиатурная навигация и фокус

Для интерактивных компонентов важно корректно управлять фокусом. Lit предоставляет методы для работы с Shadow DOM и прямой манипуляции DOM-элементами:

firstUpdated() {
  this.button = this.renderRoot.querySelector('button');
}

focusButton() {
  this.button.focus();
}

Поддержка клавиатуры требует обработки событий keydown и keyup, чтобы реализовать стандартное поведение для Enter и Space на кастомных кнопках:

_handleKeyDown(event) {
  if (event.key === 'Enter' || event.key === ' ') {
    this._triggerAction();
    event.preventDefault();
  }
}

Работа с событиями

Lit использует декларативную привязку событий через синтаксис @event, что упрощает управление обработчиками и улучшает читаемость кода.

Привязка событий в шаблоне

render() {
  return html`
    <button @click=${this._onClick}>Кликни меня</button>
  `;
}

_onClick(event) {
  console.log('Событие click:', event);
}
  • @click=${this._onClick} — привязка события к обработчику.
  • Обработчики могут быть методами класса или функциями, объявленными внутри компонента.

Кастомные события

Lit позволяет создавать и диспатчить кастомные события для взаимодействия компонентов между собой:

_handleAction() {
  this.dispatchEvent(new CustomEvent('action-performed', {
    detail: { message: 'Действие выполнено' },
    bubbles: true,
    composed: true
  }));
}
  • detail передаёт дополнительные данные.
  • bubbles позволяет событию всплывать по DOM.
  • composed делает событие доступным за пределами Shadow DOM.

Обработка событий на уровне родителя

html`<accessible-button @action-performed=${this._onAction}></accessible-button>`

_onAction(event) {
  console.log('Получено событие от дочернего компонента:', event.detail);
}

Синхронизация состояния и событий

Важной практикой является синхронизация реактивных свойств компонента с внешними событиями и атрибутами DOM. Например, изменение свойства disabled должно автоматически обновлять атрибут и визуальное состояние элемента. Lit обеспечивает это через систему реактивных свойств и requestUpdate().

set disabled(value) {
  const oldValue = this._disabled;
  this._disabled = value;
  this.requestUpdate('disabled', oldValue);
}
get disabled() {
  return this._disabled;
}

Это позволяет компоненту корректно реагировать на внешние изменения и автоматически обновлять разметку, обеспечивая согласованность состояния и доступности.

Доступность для динамического контента

Если компонент рендерит динамический контент, следует учитывать ARIA-live регионы для уведомления пользователей скринридеров о изменениях:

render() {
  return html`
    <div aria-live="polite">
      ${this._message}
    </div>
  `;
}
  • aria-live="polite" сообщает о нежных обновлениях, не прерывая текущую работу пользователя.
  • aria-live="assertive" используется для срочных уведомлений.

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