Awesomplete по умолчанию работает с одной строкой ввода, рассматривая её как единый контекст для фильтрации списка. Однако в реальных интерфейсах часто требуется поддержка множественных значений в одном input: email-адреса, теги, категории, ключевые слова. В таких случаях ввод разделяется символами-разделителями — чаще всего запятой и точкой с запятой.
Ключевая особенность обработки разделителей заключается в том, что автодополнение должно работать не со всей строкой целиком, а с текущим «активным токеном» — последним фрагментом после разделителя.
Базовый подход строится вокруг разбиения строки на части:
,;Эти символы выступают логическими границами элементов списка.
function splitInput(value) {
return value.split(/\s*[;,]\s*/).filter(Boolean);
}
Функция выполняет сразу несколько задач:
, и ;Пример:
"apple, banana; orange"
→ ["apple", "banana", "orange"]
При вводе важно отделять уже подтверждённые значения от текущего редактируемого сегмента.
function getCurrentToken(value) {
const parts = value.split(/\s*[;,]\s*/);
return parts[parts.length - 1];
}
Этот фрагмент используется как основа для фильтрации списка подсказок. Остальная часть строки считается «зафиксированной».
Стандартная логика фильтрации в Awesomplete предполагает сравнение всей строки. Для поддержки разделителей требуется переопределение фильтра:
const input = document.querySelector("#tags");
const awesomplete = new Awesomplete(input, {
filter: function(text, input) {
const token = getCurrentToken(input);
return Awesomplete.FILTER_CONTAINS(text, token);
}
});
Здесь фильтрация работает только с последним токеном, а не со всей строкой.
Наиболее критический момент — корректное добавление выбранного значения. Нужно:
awesomplete.replace = function(suggestion) {
const value = this.input.value;
const parts = value.split(/\s*[;,]\s*/);
// удаляем текущий (незавершённый) токен
parts.pop();
// добавляем выбранное значение
parts.push(suggestion);
// собираем строку обратно
this.input.value = parts.join(", ") + ", ";
};
Поведение:
Использование как запятой, так и точки с запятой требует унифицированной нормализации. Внутри интерфейса обычно выбирается один основной разделитель для вывода.
const DELIMITER = ", ";
function normalizeJoin(parts) {
return parts.filter(Boolean).join(DELIMITER);
}
При этом вход может быть смешанным:
"one, two; three, four"
→ ["one", "two", "three", "four"]
→ "one, two, three, four"
При модификации значения input возможна потеря позиции курсора, особенно при быстром наборе. Чтобы минимизировать визуальные скачки, важно фиксировать положение перед обновлением:
awesomplete.replace = function(suggestion) {
const input = this.input;
const start = input.selectionStart;
const value = input.value;
const parts = value.split(/\s*[;,]\s*/);
parts.pop();
parts.push(suggestion);
input.value = parts.join(", ") + ", ";
input.setSelectionRange(input.value.length, input.value.length);
};
Курсор перемещается в конец строки, обеспечивая непрерывный ввод следующего токена.
При массовой вставке строк с разделителями требуется предварительная нормализация:
input.addEventListener("paste", function(e) {
setTimeout(() => {
const normalized = splitInput(input.value).join(", ") + ", ";
input.value = normalized;
}, 0);
});
Логика:
При динамической загрузке списка подсказок фильтрация токенов остаётся локальной, но источник данных может обновляться:
awesomplete.list = fetch("/tags")
.then(r => r.json());
Фильтрация при этом продолжает работать только с текущим токеном, что позволяет масштабировать список без изменения логики ввода.
При работе с множественными значениями часто требуется исключать дубликаты:
function addUnique(parts, value) {
if (!parts.includes(value)) {
parts.push(value);
}
return parts;
}
Использование в replace:
parts = addUnique(parts, suggestion);
Это предотвращает повторное добавление одного и того же элемента при автодополнении.
Разделители могут сопровождаться неоднородным вводом:
Унификация достигается регулярным выражением:
const CLEAN_SPLIT = /\s*[;,]\s*/;
Она устраняет необходимость ручной очистки пробелов в нескольких местах кода.
Если последний токен пустой, автодополнение должно либо:
Типичная логика:
filter: function(text, input) {
const token = getCurrentToken(input);
if (!token) return true;
return Awesomplete.FILTER_CONTAINS(text, token);
}
Навигация стрелками не должна затрагивать уже подтверждённые элементы. Поэтому состояние списка зависит только от последнего сегмента ввода. Выбор элемента всегда влияет только на хвост строки, не изменяя ранее введённые данные.
Перед отправкой формы часто требуется финальная очистка:
function finalize(value) {
return splitInput(value)
.map(v => v.trim())
.filter(Boolean)
.join(", ");
}
Результат становится структурированным списком, готовым для серверной обработки.
Сложные случаи включают:
В таких условиях ключевой принцип сохраняется: каждый ввод интерпретируется относительно последнего разделителя, а не всей строки целиком. Это позволяет Awesomplete оставаться предсказуемой даже при нелинейном редактировании текста.