Обработка запятых и точек с запятой

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

Стандартная логика фильтрации в 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);
    }
});

Здесь фильтрация работает только с последним токеном, а не со всей строкой.


Кастомная вставка значения (replace)

Наиболее критический момент — корректное добавление выбранного значения. Нужно:

  • сохранить уже введённые элементы
  • заменить только текущий токен
  • добавить разделитель
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(", ") + ", ";
};

Поведение:

  • “apple, ba” → выбор “banana” → “apple, banana,”

Поддержка двух разделителей одновременно

Использование как запятой, так и точки с запятой требует унифицированной нормализации. Внутри интерфейса обычно выбирается один основной разделитель для вывода.

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);
});

Логика:

  • перехват события paste
  • ожидание вставки браузером
  • разбиение и пересборка строки

Комбинирование с серверными источниками данных

При динамической загрузке списка подсказок фильтрация токенов остаётся локальной, но источник данных может обновляться:

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);

Это предотвращает повторное добавление одного и того же элемента при автодополнении.


Обработка пробелов и нестандартных символов

Разделители могут сопровождаться неоднородным вводом:

  • “apple ,banana”
  • “apple; banana”
  • “apple , banana”

Унификация достигается регулярным выражением:

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 оставаться предсказуемой даже при нелинейном редактировании текста.