В стандартной конфигурации Awesomplete выбранный элемент
подставляется в <input> как строковое значение. Такой
подход подходит для одиночного выбора, но становится ограничением при
работе с множественными значениями и сложной визуализацией.
Типичный сценарий:
Для расширенной визуализации требуется отделить:
Множественный выбор реализуется через преобразование выбранных значений в отдельные визуальные блоки.
Структура интерфейса:
<div class="awesomplete-wrapper">
<div id="selected-container" class="selected-container"></div>
<input id="input" />
</div>
Каждое выбранное значение отображается как отдельный элемент:
<span class="token">
JavaScript
<button class="remove" data-value="JavaScript">×</button>
</span>
Ключевая идея:
Awesomplete продолжает отвечать за автодополнение, но вставка значения перехватывается.
const input = document.getElementById("input");
const container = document.getElementById("selected-container");
const awesomplete = new Awesomplete(input, {
list: ["JavaScript", "TypeScript", "Python", "Go"],
});
Перехват выбора:
input.addEventListener("awesomplete-selectcomplete", function (e) {
addToken(e.text.value);
input.value = "";
});
Функция визуализации выбранного элемента:
function addToken(value) {
if (isAlreadySelected(value)) return;
const token = document.createElement("span");
token.className = "token";
token.innerHTML = `
<span class="token-text">${value}</span>
<button class="remove" data-value="${value}">×</button>
`;
container.appendChild(token);
}
Состояние предотвращения дублей:
const selected = new Set();
function isAlreadySelected(value) {
return selected.has(value);
}
Обновление состояния:
function addToken(value) {
if (selected.has(value)) return;
selected.add(value);
renderToken(value);
}
Удаление должно синхронизироваться с внутренним состоянием:
container.addEventListener("click", function (e) {
if (e.target.classList.contains("remove")) {
const value = e.target.dataset.value;
selected.delete(value);
e.target.parentElement.remove();
}
});
Важный аспект:
CSS-структура определяет читаемость и UX:
.selected-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.token {
display: inline-flex;
align-items: center;
padding: 4px 8px;
background: #e0e0e0;
border-radius: 12px;
font-size: 14px;
}
.token .remove {
margin-left: 6px;
border: none;
background: transparent;
cursor: pointer;
}
Визуально важные моменты:
При отправке формы Awesomplete не хранит структуру выбранных элементов, поэтому требуется отдельное поле:
<input type="hidden" name="tags" id="tags-hidden" />
Синхронизация:
function syncHiddenInput() {
document.getElementById("tags-hidden").value =
Array.from(selected).join(",");
}
Обновление при каждом изменении:
function addToken(value) {
if (selected.has(value)) return;
selected.add(value);
renderToken(value);
syncHiddenInput();
}
function removeToken(value) {
selected.delete(value);
syncHiddenInput();
}
Визуализация выбора начинается не только после вставки, но и на этапе списка подсказок.
Переопределение item:
awesomplete.item = function (text, input) {
const li = document.createElement("li");
li.innerHTML = `
<span class="suggestion-value">${text}</span>
<span class="hint">добавить</span>
`;
return li;
};
Такой подход позволяет:
Чтобы избежать повторного выбора, элементы списка могут визуально помечаться:
awesomplete.filter = function (text, input) {
return !selected.has(text) && Awesomplete.FILTER_CONTAINS(text, input);
};
Дополнительная визуальная маркировка:
awesomplete.item = function (text) {
const li = document.createElement("li");
if (selected.has(text)) {
li.classList.add("disabled");
}
li.textContent = text;
return li;
};
CSS:
li.disabled {
opacity: 0.4;
pointer-events: none;
}
Awesomplete использует стрелочную навигацию по
<li> элементам. Для улучшения визуальной обратной
связи добавляется состояние активного элемента:
li[aria-selected="true"] {
background: #007acc;
color: white;
}
Это обеспечивает:
При использовании объектов данные отделяются от отображения:
const list = [
{ label: "JavaScript", id: 1 },
{ label: "TypeScript", id: 2 }
];
Настройка Awesomplete:
new Awesomplete(input, {
list,
label: "label",
value: "id"
});
Визуализация:
awesomplete.item = function (item) {
const li = document.createElement("li");
li.textContent = item.label;
return li;
};
Добавление токена:
function addToken(item) {
selected.add(item.id);
}
Финальная структура визуализации объединяет три слоя:
Связь между слоями строится через:
awesomplete-selectcompleteТакой подход превращает Awesomplete из простого автокомплита в компонент управления коллекциями с визуальной репрезентацией данных.