Обработчики событий в шаблонах

В Haunted обработка событий в шаблонах построена на привязке функций непосредственно к атрибутам HTML-элементов через синтаксис @event. Это позволяет компоненту реагировать на действия пользователя, такие как клики, ввод текста или наведение мыши, без необходимости вручную подписываться на события через addEventListener.

import { html, component } from 'haunted';

function Counter() {
  let count = 0;

  function increment() {
    count++;
    this.requestUpdate();
  }

  return html`
    <div>
      <p>Счётчик: ${count}</p>
      <button @click=${increment}>Увеличить</button>
    </div>
  `;
}

customElements.define('my-counter', component(Counter));

В этом примере ключевой момент — использование @click=${increment}. Haunted автоматически привязывает функцию к событию click на кнопке.

Контекст выполнения обработчика

Функция обработчика получает контекст элемента, к которому она привязана, через this. Если функция объявлена как метод компонента, this ссылается на текущий экземпляр компонента. Для стрелочных функций this наследуется из внешней области видимости, что иногда удобно для работы с локальными переменными.

function Counter() {
  let count = 0;

  const increment = () => {
    count++;
    this.requestUpdate(); // здесь this не будет ссылаться на компонент
  }

  // правильнее использовать обычную функцию
  function incrementProper() {
    count++;
    this.requestUpdate();
  }

  return html`
    <button @click=${incrementProper}>Увеличить</button>
  `;
}

Передача аргументов в обработчики

Нередко требуется передавать параметры в обработчики. В Haunted это делается через обёртку-функцию:

function Counter() {
  let count = 0;

  function add(value) {
    count += value;
    this.requestUpdate();
  }

  return html`
    <button @click=${() => add.call(this, 5)}>Добавить 5</button>
  `;
}

Использование .call(this, ...) гарантирует, что контекст обработчика будет правильным, даже если функция вызывается внутри стрелочной обёртки.

Обработчики для динамических элементов

Haunted поддерживает генерацию списков с привязкой событий к каждому элементу:

function ItemList({ items }) {
  function remove(index) {
    items.splice(index, 1);
    this.requestUpdate();
  }

  return html`
    <ul>
      ${items.map((item, i) => html`
        <li>
          ${item} <button @click=${() => remove.call(this, i)}>Удалить</button>
        </li>
      `)}
    </ul>
  `;
}

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

События с объектом Event

Обработчики получают объект события по умолчанию, что позволяет работать с его свойствами: event.target, event.preventDefault(), event.stopPropagation().

function FormComponent() {
  function handleSubmit(event) {
    event.preventDefault();
    console.log('Форма отправлена');
  }

  return html`
    <form @submit=${handleSubmit}>
      <input type="text" name="name" />
      <button type="submit">Отправить</button>
    </form>
  `;
}

Декларативная обработка нескольких событий

Haunted позволяет задавать разные события для одного элемента, просто добавляя несколько атрибутов @event:

function HoverButton() {
  function onEnter() { console.log('Мышь над кнопкой'); }
  function onLeave() { console.log('Мышь покинула кнопку'); }

  return html`
    <button @mouseenter=${onEnter} @mouseleave=${onLeave}>Наведи меня</button>
  `;
}

Поддержка пользовательских событий

Помимо стандартных DOM-событий, компоненты Haunted могут обрабатывать кастомные события:

function ChildComponent() {
  function notify() {
    this.dispatchEvent(new CustomEvent('child-action', { detail: 'данные', bubbles: true }));
  }

  return html`<button @click=${notify}>Сообщить родителю</button>`;
}

function ParentComponent() {
  function handleChildAction(event) {
    console.log('Получено событие от ребёнка:', event.detail);
  }

  return html`<child-component @child-action=${handleChildAction}></child-component>`;
}

Использование bubbles: true позволяет родительскому компоненту ловить событие, исходящее от дочернего.

Производительность и особенности

  • Обработчики привязываются один раз при рендере, повторное присваивание при каждом requestUpdate не требуется.
  • Стрелочные функции создают новый объект функции при каждом рендере, что может повлиять на производительность при больших списках. В таких случаях предпочтительно хранить методы в замыкании или как методы компонента.
  • Haunted автоматически управляет удалением обработчиков при удалении элемента из DOM, предотвращая утечки памяти.

Резюме принципов

  1. @event=${handler} — декларативная привязка событий.
  2. Контекст обработчика зависит от типа функции (обычная vs стрелочная).
  3. Передача аргументов через обёртку и .call(this).
  4. Работа с объектом Event для предотвращения стандартного поведения или получения данных.
  5. Поддержка динамических списков и пользовательских событий.
  6. Эффективное управление памятью благодаря встроенной системе Haunted.

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