В реальных интерфейсах автодополнения редко используются простые строки. Чаще данные поступают в виде объектов, содержащих несколько полей: идентификатор, отображаемое имя, дополнительную метаинформацию. Awesomplete поддерживает работу с объектами через механизм кастомного отображения и выбора значения, позволяя разделить «то, что видит пользователь» и «то, что хранится в данных».
Awesomplete может принимать массив объектов вместо массива строк:
const list = [
{ label: "JavaScript", value: "js" },
{ label: "TypeScript", value: "ts" },
{ label: "Python", value: "py" }
];
Здесь:
label — текст, который отображается в списке
автодополнения;value — значение, которое должно быть подставлено в
input.По умолчанию библиотека не «знает» про поля label и
value, поэтому требуется явно указать, как их
интерпретировать.
Awesomplete позволяет переопределить способ отображения элементов
списка с помощью функции item:
new Awesomplete(input, {
list: list,
item: function (text, input) {
const li = document.createElement("li");
li.textContent = text.label;
li.setAttribute("data-value", text.value);
return li;
}
});
Здесь каждый элемент списка создаётся вручную, что даёт полный контроль над структурой DOM.
Ключевой момент: text в данном контексте — это исходный
объект, а не строка.
Для корректной подстановки выбранного значения используется опция
replace:
new Awesomplete(input, {
list: list,
item: function (text) {
const li = document.createElement("li");
li.textContent = text.label;
li.setAttribute("data-value", text.value);
return li;
},
replace: function (text) {
this.input.value = text.value;
}
});
В этом случае:
label;value.Такой подход полезен, когда визуальное представление и внутреннее значение принципиально различаются.
Объекты могут содержать произвольные данные, не ограничиваясь двумя полями:
const list = [
{
label: "React",
value: "react",
version: "18",
type: "library"
},
{
label: "Vue",
value: "vue",
version: "3",
type: "framework"
}
];
Такая структура позволяет строить расширенные интерфейсы автодополнения, где отображается не только название, но и дополнительный контекст.
Awesomplete не ограничивает содержимое <li>,
поэтому можно визуализировать дополнительные поля:
new Awesomplete(input, {
list: list,
item: function (text) {
const li = document.createElement("li");
const title = document.createElement("span");
title.textContent = text.label;
const meta = document.createElement("small");
meta.textContent = `v${text.version} — ${text.type}`;
li.appendChild(title);
li.appendChild(meta);
return li;
},
replace: function (text) {
this.input.value = text.label;
}
});
В этом случае пользователь получает многоуровневую информацию, а не просто список значений.
Часто требуется, чтобы поиск осуществлялся по одному полю, а отображение — по другому. Awesomplete позволяет управлять этим через нормализацию списка перед передачей.
Пример подготовки данных:
const rawData = [
{ name: "JavaScript", id: 1 },
{ name: "TypeScript", id: 2 }
];
const list = rawData.map(item => ({
label: item.name,
value: item.id,
search: item.name.toLowerCase()
}));
Хотя Awesomplete не использует поле search
автоматически, его можно задействовать в кастомной фильтрации.
Стандартный механизм фильтрации Awesomplete рассчитан на строки. Для
объектов его можно переопределить через filter:
new Awesomplete(input, {
list: list,
filter: function (text, input) {
const value = input.trim().toLowerCase();
return text.label.toLowerCase().includes(value);
}
});
Теперь поиск выполняется по label, независимо от
структуры объекта.
Дополнительно можно управлять порядком выдачи через
sort:
new Awesomplete(input, {
list: list,
filter: function (text, input) {
return text.label.toLowerCase().includes(input.toLowerCase());
},
sort: function (a, b) {
return a.label.length - b.label.length;
}
});
В этом примере более короткие совпадения отображаются выше.
Частый сценарий — сохранение ID, а не текста:
const list = [
{ label: "Москва", id: 101 },
{ label: "Санкт-Петербург", id: 102 }
];
new Awesomplete(input, {
list: list,
item: function (text) {
const li = document.createElement("li");
li.textContent = text.label;
return li;
},
replace: function (text) {
this.input.value = text.label;
this.input.dataset.id = text.id;
}
});
Таким образом:
data-id.Awesomplete позволяет изменять список после инициализации:
awesomplete.list = [
{ label: "Go", value: "go" },
{ label: "Rust", value: "rust" }
];
При обновлении важно сохранять структуру объектов, иначе кастомные
item, filter и replace перестанут
работать корректно.
Типичный сценарий — загрузка объектов с сервера:
fetch("/api/languages")
.then(res => res.json())
.then(data => {
awesomplete.list = data.map(item => ({
label: item.name,
value: item.slug,
id: item.id
}));
});
Здесь преобразование данных является обязательным этапом, так как Awesomplete не нормализует серверные ответы автоматически.
Иногда данные приходят в виде вложенных структур:
const list = [
{
label: "Frontend",
items: [
{ label: "React", value: "react" },
{ label: "Vue", value: "vue" }
]
}
];
Awesomplete не поддерживает группировку напрямую, поэтому такие данные требуют предварительного «разворачивания»:
const flatList = list.flatMap(group => group.items);
После этого можно использовать стандартные механизмы отображения.
При использовании пользовательских данных важно учитывать потенциальные XSS-риски при вставке HTML:
li.innerHTML = text.label;
Такой подход допустим только при гарантированной очистке данных. Более безопасный вариант:
li.textContent = text.label;
Для сложных интерфейсов следует использовать явное создание DOM-узлов
вместо innerHTML.
Ключевым требованием при работе с объектами в Awesomplete является неизменность структуры элементов списка. Все функции:
itemreplacefiltersortдолжны опираться на одинаковую модель объекта. Любое несоответствие приводит к некорректной работе автодополнения, особенно при динамическом обновлении списка.