Одной из ключевых возможностей автодополнения является динамическое
изменение списка вариантов без перезагрузки страницы. В библиотеке
Awesomplete это реализуется через изменение свойства list у
экземпляра компонента.
В отличие от статического массива, заданного при инициализации, динамический список позволяет:
После создания экземпляра Awesomplete список можно изменить в любой момент.
<input id="cities">
<script>
const input = document.getElementById("cities");
const awesomplete = new Awesomplete(input, {
minChars: 1
});
awesomplete.list = [
"Алматы",
"Астана",
"Караганда"
];
</script>
Позднее список заменяется новым набором данных:
awesomplete.list = [
"Москва",
"Минск",
"Ташкент"
];
После присвоения нового массива компонент автоматически перестраивает внутренний список подсказок.
Наиболее распространённый сценарий — изменение данных при каждом вводе символа.
<input id="search">
const input = document.getElementById("search");
const awesomplete = new Awesomplete(input, {
minChars: 1
});
const database = [
"JavaScript",
"Java",
"Python",
"PHP",
"Perl",
"Rust",
"Ruby"
];
input.addEventListener("input", () => {
const value = input.value.toLowerCase();
const filtered = database.filter(item =>
item.toLowerCase().includes(value)
);
awesomplete.list = filtered;
});
В этом примере:
input запускает обработчик;Awesomplete особенно полезен при интеграции с сервером.
const input = document.getElementById("users");
const awesomplete = new Awesomplete(input, {
minChars: 2
});
input.addEventListener("input", async () => {
const query = input.value;
if(query.length < 2) {
return;
}
const response = await fetch(
`/api/users?q=${encodeURIComponent(query)}`
);
const users = await response.json();
awesomplete.list = users;
});
Сервер может возвращать массив:
[
"Alex",
"Alice",
"Albert"
]
После получения ответа список моментально обновляется.
При быстром вводе нельзя отправлять запрос на сервер после каждого символа. Это создаёт:
Для решения используется debounce.
function debounce(callback, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
callback.apply(this, args);
}, delay);
};
}
Использование:
const handler = debounce(async () => {
const query = input.value;
if(query.length < 2) {
return;
}
const response = await fetch(
`/search?q=${encodeURIComponent(query)}`
);
const data = await response.json();
awesomplete.list = data;
}, 300);
input.addEventListener("input", handler);
Теперь запрос отправляется только после паузы во вводе.
Без дополнительной обработки подсказки могут оставаться видимыми даже после удаления текста.
Правильная реализация:
input.addEventListener("input", () => {
const value = input.value.trim();
if(value === "") {
awesomplete.list = [];
awesomplete.close();
return;
}
});
Метод close() скрывает выпадающий список.
Awesomplete поддерживает не только строки, но и объекты.
awesomplete.list = [
{
label: "JavaScript",
value: "js"
},
{
label: "Python",
value: "py"
}
];
Настройка отображения:
const awesomplete = new Awesomplete(input, {
item: function(item, value) {
return Awesomplete.ITEM(
item.label,
value
);
},
replace: function(item) {
this.input.value = item.label;
}
});
Динамическое обновление объектов выполняется аналогично:
awesomplete.list = newData;
Awesomplete можно связать с постоянным соединением.
const socket = new WebSocket("wss://example.com");
socket.addEventListener("message", event => {
const data = JSON.parse(event.data);
awesomplete.list = data;
});
Такой подход используется в:
Иногда требуется не заменить весь массив, а дополнить существующий.
const current = awesomplete._list || [];
const updated = [
...current,
"Новый элемент"
];
awesomplete.list = updated;
Однако использование _list нежелательно, так как это
внутреннее свойство библиотеки.
Безопаснее хранить данные отдельно:
let items = [
"HTML",
"CSS"
];
const awesomplete = new Awesomplete(input);
awesomplete.list = items;
function addItem(value) {
items.push(value);
awesomplete.list = items;
}
Иногда содержимое одного поля зависит от другого.
<select id="country">
<option value="kz">Казахстан</option>
<option value="ru">Россия</option>
</select>
<input id="city">
const cities = {
kz: [
"Алматы",
"Астана",
"Шымкент"
],
ru: [
"Москва",
"Казань",
"Новосибирск"
]
};
const country = document.getElementById("country");
const awesomplete = new Awesomplete(
document.getElementById("city")
);
country.addEventListener("change", () => {
const value = country.value;
awesomplete.list = cities[value];
});
Список городов изменяется в зависимости от выбранной страны.
После изменения списка иногда требуется немедленно показать подсказки.
awesomplete.list = data;
awesomplete.evaluate();
Метод evaluate() инициирует повторную проверку
совпадений и открывает выпадающее меню.
При обновлении тысяч элементов могут появляться:
const filtered = database
.filter(item =>
item.includes(query)
)
.slice(0, 20);
awesomplete.list = filtered;
Чтобы не отправлять одинаковые запросы повторно, используется кэш.
const cache = {};
input.addEventListener("input", async () => {
const query = input.value;
if(cache[query]) {
awesomplete.list = cache[query];
return;
}
const response = await fetch(`/search?q=${query}`);
const data = await response.json();
cache[query] = data;
awesomplete.list = data;
});
Преимущества:
При быстром вводе более старый запрос может завершиться позже нового и перезаписать актуальные данные.
Решение — AbortController.
let controller;
input.addEventListener("input", async () => {
if(controller) {
controller.abort();
}
controller = new AbortController();
const signal = controller.signal;
try {
const response = await fetch("/search", {
signal
});
const data = await response.json();
awesomplete.list = data;
} catch(error) {
if(error.name !== "AbortError") {
console.error(error);
}
}
});
Во время получения данных полезно показывать пользователю промежуточное состояние.
awesomplete.list = [
"Загрузка..."
];
После завершения запроса:
awesomplete.list = data;
При ошибке:
awesomplete.list = [
"Ошибка загрузки"
];
Awesomplete позволяет полностью переопределить механизм поиска.
const awesomplete = new Awesomplete(input, {
filter: function(text, input) {
return text.startsWith(input);
}
});
Теперь после обновления списка будут отображаться только элементы, начинающиеся с введённой строки.
Типичный сценарий:
input.addEventListener("input", async () => {
const query = input.value;
const response = await fetch(
`https://api.example.com/search?q=${query}`
);
const json = await response.json();
const items = json.results.map(item => item.name);
awesomplete.list = items;
});
Серверная структура может быть сложной, поэтому данные обычно преобразуются перед передачей в Awesomplete.
Если список обновляется слишком часто, выпадающее меню может визуально «прыгать».
Для уменьшения проблемы применяются:
Пример проверки:
function arraysEqual(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
if(!arraysEqual(currentList, newList)) {
currentList = newList;
awesomplete.list = newList;
}
Иногда часть информации хранится локально, а часть загружается с сервера.
const local = [
"HTML",
"CSS"
];
const remote = await fetchData();
awesomplete.list = [
...local,
...remote
];
Данные могут обновляться автоматически через интервалы.
setInterval(async () => {
const response = await fetch("/popular");
const data = await response.json();
awesomplete.list = data;
}, 5000);
Такой механизм подходит для:
Иногда проще создать новый экземпляр Awesomplete.
let awesomplete =
new Awesomplete(input);
Позднее:
awesomplete = new Awesomplete(input, {
list: newData
});
Такой подход применяется редко, поскольку обновление
list обычно эффективнее.
Если DOM-элемент пересоздаётся, поле ввода может потерять курсор.
Без debounce приложение способно генерировать десятки запросов в секунду.
Старые ответы могут перезаписывать новые результаты.
Большие массивы ухудшают производительность.
Awesomplete ожидает массив строк либо объектов корректной структуры.
Ошибка:
awesomplete.list = {
name: "JavaScript"
};
Правильно:
awesomplete.list = [
"JavaScript"
];
Полноценная система обновления подсказок обычно включает:
Такой подход позволяет построить быстрый, отзывчивый и масштабируемый интерфейс автодополнения на базе Awesomplete.