Архитектура расширяемости Awesomplete опирается на комбинацию конфигурационных колбэков и DOM-событий, формируя двухуровневую систему перехвата поведения: внутренние точки расширения (через опции конструктора) и внешние (через события и модификацию экземпляра). Такая модель позволяет изменять практически любой этап жизненного цикла автодополнения без форка библиотеки.
Работа Awesomplete начинается с обработки входного значения и списка данных, после чего проходит несколько стадий трансформации:
Каждая стадия имеет собственные перехваты, которые можно заменить или дополнить.
Ключевая особенность заключается в том, что Awesomplete не скрывает свой pipeline — он параметризован через набор функций, передаваемых в конструктор.
Наиболее важные точки расширения реализованы через свойства экземпляра:
filtersortitemreplacedataЭти функции образуют ядро кастомизации.
Функция фильтрации определяет, какие элементы списка попадут в результат.
awesomplete.filter = function(text, input) {
return text.indexOf(input) !== -1;
};
Фильтр вызывается для каждого элемента исходного массива. Важно, что Awesomplete не навязывает формат сравнения: можно реализовать:
Пример расширенной логики:
awesomplete.filter = function(text, input) {
const normalize = s => s.toLowerCase().trim();
return normalize(text).includes(normalize(input));
};
Таким образом фильтр становится полноценной точкой внедрения поискового алгоритма.
После фильтрации элементы проходят стадию упорядочивания.
awesomplete.sort = function(a, b) {
return a.length - b.length;
};
Сортировка может учитывать:
Расширенный вариант — приоритет совпадения начала строки:
awesomplete.sort = function(a, b) {
const input = this.input.value.toLowerCase();
const ap = a.toLowerCase().indexOf(input) === 0 ? 0 : 1;
const bp = b.toLowerCase().indexOf(input) === 0 ? 0 : 1;
return ap - bp || a.length - b.length;
};
Здесь сортировка становится контекстной, зависящей от текущего ввода.
Одна из самых мощных точек расширения — функция item,
отвечающая за создание элемента списка.
awesomplete.item = function(text, input) {
const li = document.createElement("li");
li.textContent = text;
return li;
};
Через неё можно полностью изменить визуальное представление:
Пример подсветки:
awesomplete.item = function(text, input) {
const li = document.createElement("li");
const regex = new RegExp(input, "gi");
li.innerHTML = text.replace(regex, match => `<mark>${match}</mark>`);
return li;
};
Важно учитывать, что использование innerHTML открывает
возможность XSS, если источник данных не контролируется.
Функция replace определяет, как выбранный элемент
вставляется в input.
awesomplete.replace = function(text) {
this.input.value = text;
};
Это позволяет:
Пример с нормализацией:
awesomplete.replace = function(text) {
this.input.value = text.split(" — ")[0];
};
Здесь отображаемое значение может содержать дополнительную информацию, но в input попадает только основная часть.
dataОпция data контролирует, как элементы списка
интерпретируются внутри библиотеки.
awesomplete.data = function(item) {
return {
label: item.name,
value: item.id
};
};
Это особенно важно при работе с объектами:
const list = [
{ id: 1, name: "Almaty" },
{ id: 2, name: "Astana" }
];
Awesomplete по умолчанию ожидает строки, но через data
можно внедрить полноценные структуры.
Помимо колбэков существует система DOM-событий. Они позволяют реагировать на изменения состояния без модификации экземпляра.
Основные события:
awesomplete-openawesomplete-closeawesomplete-selectawesomplete-selectcompleteawesomplete-highlightinput.addEventListener("awesomplete-open", function() {
console.log("Список открыт");
});
Это событие полезно для:
input.addEventListener("awesomplete-close", function() {
console.log("Список закрыт");
});
Используется для очистки временного состояния или сохранения пользовательского выбора.
Событие awesomplete-select вызывается до финальной
подстановки значения.
input.addEventListener("awesomplete-select", function(event) {
console.log(event.text);
});
Здесь можно:
Событие awesomplete-selectcomplete происходит после
завершения вставки.
input.addEventListener("awesomplete-highlight", function(event) {
console.log(event.text);
});
Позволяет реагировать на навигацию по списку стрелками без выбора.
Хотя Awesomplete не поощряет изменение внутреннего API, возможно расширение через прототип:
Awesomplete.prototype.open = function() {
console.log("Переопределено");
Awesomplete.prototype.constructor.prototype.open.call(this);
};
Такой подход используется редко, но позволяет:
Однако он создаёт риск несовместимости при обновлениях.
Одним из ключевых сценариев расширения является замена статического списка на динамический источник.
awesomplete.list = [];
И последующее обновление:
fetch("/api/suggestions?q=" + input.value)
.then(r => r.json())
.then(data => {
awesomplete.list = data;
awesomplete.evaluate();
});
Здесь Awesomplete используется как UI-слой, а не как источник данных.
В сложных приложениях фильтр превращается в отдельный модуль.
Пример гибридного поиска:
awesomplete.filter = function(text, input) {
const score = fuzzyScore(text, input);
return score > 0.5;
};
Где fuzzyScore может учитывать:
Таким образом Awesomplete становится оболочкой для произвольного поискового движка.
Функция item часто используется для внедрения сложного
UI:
awesomplete.item = function(text) {
const li = document.createElement("li");
const wrapper = document.createElement("div");
wrapper.className = "suggestion";
const title = document.createElement("span");
title.textContent = text;
const icon = document.createElement("i");
icon.className = "icon";
wrapper.appendChild(icon);
wrapper.appendChild(title);
li.appendChild(wrapper);
return li;
};
Это позволяет интегрировать:
Через комбинацию событий и replace можно реализовать
нестандартные сценарии:
Пример multi-value:
awesomplete.replace = function(text) {
const values = this.input.value.split(",");
values[values.length - 1] = text;
this.input.value = values.join(",");
};
На практике расширения комбинируются:
Такая композиция позволяет строить поверх Awesomplete полноценные поисковые интерфейсы без изменения ядра библиотеки.