Логирование событий

Библиотека Awesomplete предоставляет набор событий, позволяющих отслеживать весь жизненный цикл автодополнения: от ввода текста до выбора элемента и закрытия списка подсказок. Логирование этих событий превращает компонент в прозрачный инструмент, пригодный для отладки, аналитики поведения пользователей и построения расширенной бизнес-логики поверх стандартного UI.

Каждое событие в Awesomplete диспатчится на исходный input-элемент через CustomEvent, что делает механизм интеграции событий предсказуемым и совместимым с нативным DOM API.


Базовая архитектура событий Awesomplete

Awesomplete генерирует события на уровне input-поля, к которому привязан экземпляр автодополнения. Подписка осуществляется стандартным способом через addEventListener.

Основные события:

  • awesomplete-open — список подсказок открыт
  • awesomplete-close — список закрыт
  • awesomplete-select — пользователь выбрал элемент (до подстановки значения)
  • awesomplete-selectcomplete — выбор завершён, значение установлено в input
  • awesomplete-highlight — изменение активного (подсвеченного) элемента
  • input — нативное событие ввода, часто используется в связке

Каждое событие содержит объект event.detail, который предоставляет контекст происходящего.


Подключение базового логирования событий

Логирование событий строится через единый обработчик, регистрируемый на input:

const input = document.querySelector("#search");
const awesomplete = new Awesomplete(input, {
  list: ["Apple", "Apricot", "Banana", "Blueberry", "Cherry"]
});

function logEvent(name) {
  return function (event) {
    console.log(`[Awesomplete:${name}]`, event.detail);
  };
}

input.addEventListener("awesomplete-open", logEvent("open"));
input.addEventListener("awesomplete-close", logEvent("close"));
input.addEventListener("awesomplete-select", logEvent("select"));
input.addEventListener("awesomplete-selectcomplete", logEvent("selectcomplete"));
input.addEventListener("awesomplete-highlight", logEvent("highlight"));

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


Структура event.detail и полезные поля

Содержимое event.detail зависит от конкретного события, но обычно включает:

  • text — выбранный или подсвеченный элемент
  • value — финальное значение для input
  • item — DOM-узел элемента списка
  • index — позиция элемента в списке
  • originalEvent — нативное событие (клавиатура, мышь)

Пример анализа выбора:

input.addEventListener("awesomplete-selectcomplete", (event) => {
  const { text, value } = event.detail;

  console.log("Выбран текст:", text);
  console.log("Установленное значение:", value);
});

Логирование переходов состояния (state tracing)

Awesomplete можно рассматривать как конечный автомат, где события отражают переходы между состояниями:

  • closed → open
  • open → highlight change
  • open → select
  • select → close

Для трассировки состояния удобно вести журнал:

const stateLog = [];

function trace(state) {
  return (event) => {
    stateLog.push({
      state,
      time: Date.now(),
      detail: event.detail
    });
  };
}

input.addEventListener("awesomplete-open", trace("open"));
input.addEventListener("awesomplete-close", trace("close"));
input.addEventListener("awesomplete-highlight", trace("highlight"));
input.addEventListener("awesomplete-selectcomplete", trace("selectcomplete"));

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


Разделение логов по уровням важности

При интеграции в крупные системы логирование событий автодополнения обычно разделяется на уровни:

  • DEBUG — все события Awesomplete
  • INFO — выбор значений и открытия списка
  • WARN — неожиданные состояния (например, пустой список)
  • ERROR — неконсистентные данные или сбои интеграции

Реализация уровневого логирования:

const Logger = {
  debug: (msg, data) => console.debug(msg, data),
  info: (msg, data) => console.info(msg, data),
  warn: (msg, data) => console.warn(msg, data),
  error: (msg, data) => console.error(msg, data)
};

input.addEventListener("awesomplete-open", (e) => {
  Logger.debug("dropdown opened", e.detail);
});

input.addEventListener("awesomplete-selectcomplete", (e) => {
  Logger.info("value selected", e.detail);
});

Интеграция с аналитикой пользовательского поведения

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

Типовые метрики:

  • частота открытия списка
  • время до выбора элемента
  • процент выбора через клавиатуру vs мышь
  • количество отказов (open → close без select)

Пример отправки событий в абстрактный трекер:

function track(eventName, payload) {
  analytics.send(eventName, payload);
}

input.addEventListener("awesomplete-open", () => {
  track("autocomplete_open", { field: "search" });
});

input.addEventListener("awesomplete-selectcomplete", (e) => {
  track("autocomplete_select", {
    value: e.detail.value
  });
});

Определение способа взаимодействия (keyboard vs mouse)

Awesomplete передаёт исходное событие в originalEvent, что позволяет различать источник действия.

input.addEventListener("awesomplete-selectcomplete", (event) => {
  const origin = event.detail.originalEvent;

  const method = origin && origin.type === "keydown"
    ? "keyboard"
    : "mouse";

  console.log("Метод выбора:", method);
});

Это особенно важно для UX-аналитики, где поведение клавиатурных пользователей отличается от мышиных сценариев.


Централизованный перехват всех событий

Вместо множественных подписок возможно создание универсального слушателя:

const events = [
  "awesomplete-open",
  "awesomplete-close",
  "awesomplete-select",
  "awesomplete-selectcomplete",
  "awesomplete-highlight"
];

events.forEach(eventName => {
  input.addEventListener(eventName, (event) => {
    console.log(eventName, event.detail);
  });
});

Такой подход снижает вероятность пропуска новых событий при расширении логики.


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

Для системного подхода применяется декоратор экземпляра:

function createLoggedAwesomplete(input, config) {
  const instance = new Awesomplete(input, config);

  const events = [
    "awesomplete-open",
    "awesomplete-close",
    "awesomplete-select",
    "awesomplete-selectcomplete",
    "awesomplete-highlight"
  ];

  events.forEach(name => {
    input.addEventListener(name, (event) => {
      console.log(`[AWSP:${name}]`, event.detail);
    });
  });

  return instance;
}

Такой слой позволяет внедрять логирование без изменения остального кода приложения.


Ошибочные состояния и диагностическое логирование

Несмотря на простоту Awesomplete, ошибки часто возникают на уровне данных:

  • пустой или невалидный список
  • несоответствие отображаемого текста и значения
  • асинхронное обновление списка без синхронизации

Для диагностики полезно отслеживать состояние списка перед открытием:

input.addEventListener("awesomplete-open", (event) => {
  const instance = event.target.awesomplete;

  if (!instance.list || instance.list.length === 0) {
    console.warn("Awesomplete открыт без данных");
  }
});

Связь логирования с производительностью

Избыточное логирование может влиять на производительность, особенно при частых событиях highlight и input.

Оптимизация включает:

  • отключение debug-логов в production
  • буферизацию логов
  • debounce для частых событий

Пример debounce:

function debounce(fn, delay) {
  let t;
  return (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), delay);
  };
}

input.addEventListener("awesomplete-highlight",
  debounce((e) => {
    console.log("highlight", e.detail);
  }, 100)
);

Формирование структурированного журнала событий

Для долговременного хранения событий формируется единый формат записи:

function createLogEntry(type, detail) {
  return {
    type,
    timestamp: performance.now(),
    payload: detail
  };
}

И применение:

input.addEventListener("awesomplete-selectcomplete", (e) => {
  const entry = createLogEntry("selectcomplete", e.detail);
  logs.push(entry);
});

Такой журнал становится основой для последующего анализа поведения автодополнения в интерфейсе.