Решение проблем инициализации

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

Классический сценарий ошибки:

<script src="awesomplete.min.js"></script>
<script>
const input = document.querySelector("#city");
new Awesomplete(input, {
  list: ["Paris", "London", "Berlin"]
});
</script>

<input id="city" />

Здесь document.querySelector("#city") возвращает null, поскольку элемент ещё не создан в DOM на момент выполнения скрипта.

Корректный порядок:

<input id="city" />

<script src="awesomplete.min.js"></script>
<script>
const input = document.querySelector("#city");

if (input) {
  new Awesomplete(input, {
    list: ["Paris", "London", "Berlin"]
  });
}
</script>

Ключевой момент заключается в том, что инициализация должна происходить только после гарантированного появления DOM-узла.


Инициализация после загрузки DOM

В проектах с более сложной структурой HTML безопаснее использовать событие DOMContentLoaded, исключающее гонки загрузки:

document.addEventListener("DOMContentLoaded", () => {
  const input = document.querySelector("#city");

  new Awesomplete(input, {
    list: ["Paris", "London", "Berlin"]
  });
});

Этот подход особенно важен при подключении скриптов в <head>, когда DOM ещё не построен.


Проблемы с отсутствием элемента input

Awesomplete строго привязан к конкретному HTMLInputElement или HTMLTextAreaElement. Передача любого другого типа узла приводит к некорректной работе.

Типичный дефект:

const container = document.querySelector(".search-box");

new Awesomplete(container, {
  list: ["Apple", "Banana"]
});

В данном случае контейнер не является полем ввода, поэтому библиотека не может навесить события input, focus, keydown.

Корректный вариант:

const input = document.querySelector(".search-box input");

new Awesomplete(input, {
  list: ["Apple", "Banana"]
});

Ошибки при отсутствии списка данных

Awesomplete допускает пустую инициализацию, но отсутствие корректного list приводит к тому, что автодополнение не работает, хотя экземпляр создаётся без ошибок.

Сценарий проблемы:

new Awesomplete(document.querySelector("#city"), {
  list: null
});

Библиотека ожидает массив или объект-источник. Неправильный формат приводит к отсутствию подсказок без явного уведомления.

Правильные варианты:

list: []

или

list: ["Paris", "London"]

или динамическая подача:

list: fetchCities()

где fetchCities() возвращает массив синхронно или уже подготовленные данные.


Асинхронная загрузка данных и поздняя инициализация

Частая проблема возникает при попытке использовать данные, которые загружаются через fetch.

Ошибочный подход:

const data = [];

fetch("/cities.json")
  .then(r => r.json())
  .then(json => {
    data.push(...json);
  });

new Awesomplete(document.querySelector("#city"), {
  list: data
});

Инициализация происходит до заполнения массива, поэтому список оказывается пустым.

Корректное решение — инициализация после получения данных:

fetch("/cities.json")
  .then(r => r.json())
  .then(data => {
    new Awesomplete(document.querySelector("#city"), {
      list: data
    });
  });

Альтернативный подход — обновление списка через API экземпляра:

const input = document.querySelector("#city");
const awesomplete = new Awesomplete(input, { list: [] });

fetch("/cities.json")
  .then(r => r.json())
  .then(data => {
    awesomplete.list = data;
  });

Повторная инициализация одного и того же элемента

Awesomplete не предназначен для многократного создания экземпляра на одном input. Повторная инициализация приводит к наложению обработчиков событий и некорректному поведению списка.

Проблемный код:

const input = document.querySelector("#city");

new Awesomplete(input, { list: ["A", "B"] });
new Awesomplete(input, { list: ["C", "D"] });

В результате могут возникать дубли подсказок, конфликт фокуса и некорректная навигация.

Правильная стратегия — хранение экземпляра:

const input = document.querySelector("#city");

const awesomplete = new Awesomplete(input, {
  list: ["A", "B"]
});

И дальнейшее обновление через свойства экземпляра.


Конфликты с другими библиотеками

Awesomplete использует стандартные DOM-события: input, keydown, blur. Конфликты возникают при наличии других библиотек автодополнения (например, jQuery UI Autocomplete или кастомных обработчиков).

Типичный симптом — подсказки не отображаются или исчезают сразу после появления.

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

input.addEventListener("keydown", (e) => {
  e.stopPropagation();
});

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

Решение заключается в контроле порядка навешивания обработчиков. Awesomplete должен инициализироваться после всех кастомных событийных слоёв или изолироваться:

setTimeout(() => {
  new Awesomplete(input, { list: ["A", "B"] });
}, 0);

Проблемы в модульных системах (ESM, CommonJS)

При использовании сборщиков (Webpack, Vite, Rollup) возникает ситуация, когда библиотека импортируется некорректно или используется до завершения гидратации модуля.

Ошибка импорта:

import Awesomplete from "awesomplete";

В некоторых конфигурациях библиотека экспортируется как глобальный объект, а не модуль, что приводит к undefined.

Рабочие варианты:

import "awesomplete/awesomplete.js";

или использование глобального объекта:

const Awesomplete = window.Awesomplete;

Ошибки при серверном рендеринге (SSR)

В средах SSR (например, Next.js или Nuxt) DOM отсутствует, поэтому любая попытка обращения к document вызывает падение.

Проблемный код:

const input = document.querySelector("#city");
new Awesomplete(input, { list: [] });

Такой код выполняется на сервере и приводит к ReferenceError: document is not defined.

Корректный подход — отложенная инициализация:

if (typeof window !== "undefined") {
  const input = document.querySelector("#city");

  if (input) {
    new Awesomplete(input, { list: [] });
  }
}

В React-подобных системах дополнительно используется lifecycle:

useEffect(() => {
  const input = document.querySelector("#city");

  new Awesomplete(input, { list: [] });
}, []);

Проблемы с повторным рендерингом DOM

В динамических интерфейсах элемент input может пересоздаваться (например, при обновлении состояния UI). В таком случае старый экземпляр Awesomplete остаётся привязанным к удалённому узлу.

Симптомы:

  • подсказки не появляются
  • события не срабатывают
  • консоль не показывает ошибок

Решение заключается в повторной привязке:

let awesomplete;

function init() {
  const input = document.querySelector("#city");

  awesomplete = new Awesomplete(input, {
    list: ["Paris", "Berlin"]
  });
}

function reinit() {
  if (awesomplete) {
    awesomplete.destroy?.();
  }

  init();
}

Ошибки конфигурации параметров при инициализации

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

Проблемные конфигурации:

new Awesomplete(input, {
  minChars: "3",
  maxItems: "10"
});

Здесь числовые параметры переданы как строки, что приводит к некорректным сравнениям внутри библиотеки.

Правильный вариант:

new Awesomplete(input, {
  minChars: 3,
  maxItems: 10
});

Инициализация в скрытых или неактивных элементах

Если input находится внутри скрытого контейнера (display: none), Awesomplete может некорректно рассчитать позицию списка подсказок.

Симптом:

  • список не отображается
  • позиционирование сбивается
  • dropdown появляется в углу страницы

Решение — инициализация после отображения элемента:

container.style.display = "block";

requestAnimationFrame(() => {
  new Awesomplete(input, { list: ["A", "B"] });
});

Потеря контекста экземпляра

В сложных приложениях экземпляр Awesomplete может теряться при передаче функций-обработчиков.

Проблемный код:

function initAwesomplete() {
  const input = document.querySelector("#city");

  const awesomplete = new Awesomplete(input, {
    list: ["Paris", "London"]
  });
}

button.addEventListener("click", initAwesomplete);

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

Решение — хранение ссылки вне функции:

let awesomplete;

function initAwesomplete() {
  const input = document.querySelector("#city");

  if (awesomplete) {
    awesomplete.destroy?.();
  }

  awesomplete = new Awesomplete(input, {
    list: ["Paris", "London"]
  });
}