Вставка частичных значений

В типичных сценариях автодополнения пользователь вводит часть строки, после чего система подставляет выбранный элемент целиком. Однако в реальных интерфейсах часто требуется более гибкая логика: вставка не полного значения, а только его части, либо замена конкретного фрагмента внутри составного текста. Библиотека Awesomplete позволяет управлять этим процессом через переопределение поведения вставки и работу с функцией replace.


Базовый принцип подстановки значения

По умолчанию Awesomplete использует простую стратегию: при выборе элемента выпадающего списка текущее содержимое input полностью заменяется значением выбранного элемента.

Пример стандартного поведения:

const input = document.querySelector("#city");

new Awesomplete(input, {
  list: ["Almaty", "Astana", "Shymkent"]
});

При выборе Astana текущее значение поля становится строго Astana, независимо от уже введённого текста.

Такой подход подходит для одиночных значений, но становится ограничением при работе с составными строками.


Частичная замена через Awesomplete.replace

Ключевая точка расширения логики вставки — метод replace. Он определяет, каким образом выбранный элемент списка преобразуется в значение input.

Сигнатура поведения задаётся через опцию:

new Awesomplete(input, {
  list: ["JavaScript", "Java", "Python"],

  replace: function (suggestion) {
    this.input.value = suggestion;
  }
});

Здесь suggestion — выбранный элемент списка, а this.input.value — поле ввода.

Однако основная сила replace проявляется тогда, когда необходимо работать не с полным значением, а с его частью.


Вставка части слова вместо полного значения

Одна из распространённых задач — замена только последнего введённого фрагмента, а не всей строки.

Пример: ввод нескольких языков через запятую.

const input = document.querySelector("#langs");

new Awesomplete(input, {
  list: ["JavaScript", "Java", "Python", "PHP"],

  replace: function (suggestion) {
    const value = this.input.value;

    const parts = value.split(",");
    parts.pop();

    parts.push(suggestion);

    this.input.value = parts.join(", ");
  }
});

Здесь реализуется ключевой принцип частичной вставки:

  • текущее значение разбивается по разделителю
  • последний фрагмент считается активным вводом
  • он заменяется выбранным значением
  • остальная часть строки сохраняется

Такой подход используется в тегах, списках навыков, email-адресах и фильтрах.


Контроль позиции вставки

Частичная замена может быть привязана не только к последнему элементу, но и к позиции курсора.

replace: function (suggestion) {
  const input = this.input;
  const start = input.selectionStart;
  const end = input.selectionEnd;

  const before = input.value.substring(0, start);
  const after = input.value.substring(end);

  input.value = before + suggestion + after;
}

Такой механизм позволяет:

  • вставлять значения внутрь текста
  • заменять выделенный фрагмент
  • работать как с мини-редактором

Awesomplete в этом случае выступает не просто автодополнением, а инструментом контекстной вставки.


Работа с токенами и префиксами

Часто ввод содержит структурированные данные: ключевые слова, хэштеги, параметры запроса.

Пример обработки токена #:

new Awesomplete(input, {
  list: ["javascript", "webdev", "css", "html"],

  replace: function (suggestion) {
    const value = this.input.value;
    const hashIndex = value.lastIndexOf("#");

    this.input.value =
      value.substring(0, hashIndex + 1) + suggestion + " ";
  }
});

Поведение:

  • ищется последний символ #
  • заменяется только часть после него
  • остальной текст сохраняется

Это позволяет строить системы хэштегов и командного ввода.


Разделение отображаемого и вставляемого значения

Awesomplete поддерживает объекты в списке, где отображаемый текст и фактическое значение различаются.

new Awesomplete(input, {
  list: [
    { label: "United States", value: "US" },
    { label: "United Kingdom", value: "UK" }
  ],

  replace: function (item) {
    this.input.value = item.value;
  }
});

В этом случае:

  • label отображается пользователю
  • value используется при вставке

Частичная вставка может использовать оба поля одновременно:

replace: function (item) {
  const parts = this.input.value.split(":");
  parts[1] = item.value;
  this.input.value = parts.join(":");
}

Вставка в многострочные поля

При работе с textarea логика частичной подстановки усложняется из-за наличия переносов строк.

replace: function (suggestion) {
  const lines = this.input.value.split("\n");
  const lastLineIndex = lines.length - 1;

  lines[lastLineIndex] = suggestion;

  this.input.value = lines.join("\n");
}

Такой подход полезен для:

  • генерации конфигураций
  • заполнения списков
  • редактирования шаблонов

Нормализация пробелов при вставке

При частичной подстановке часто возникает проблема лишних пробелов и запятых.

replace: function (suggestion) {
  let value = this.input.value;

  value = value.replace(/\s*,\s*$/, "");

  this.input.value = value + ", " + suggestion;
}

Здесь выполняется нормализация:

  • удаляются хвостовые разделители
  • добавляется единый формат вставки
  • поддерживается чистая структура строки

Использование пользовательской логики выделения фрагмента

Awesomplete не ограничивает способ определения заменяемого фрагмента. Логика может полностью зависеть от контекста ввода.

Пример выделения слова под курсором:

function getWordBounds(value, position) {
  const left = value.slice(0, position).search(/\S+$/);
  const right = value.slice(position).search(/\s/);

  return {
    start: left === -1 ? 0 : left,
    end: right === -1 ? value.length : position + right
  };
}

Использование в replace:

replace: function (suggestion) {
  const input = this.input;
  const pos = input.selectionStart;

  const bounds = getWordBounds(input.value, pos);

  input.value =
    input.value.slice(0, bounds.start) +
    suggestion +
    input.value.slice(bounds.end);
}

Сценарии комбинированной вставки

Частичная подстановка часто сочетается с другими функциями:

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

Пример динамического расширения:

new Awesomplete(input, {
  list: [],

  replace: function (suggestion) {
    const parts = this.input.value.split(" ");

    parts[parts.length - 1] = suggestion;
    this.input.value = parts.join(" ");
  },

  data: function (item) {
    return item.toUpperCase();
  }
});

Здесь вставка работает совместно с трансформацией данных.


Поведение при кастомных разделителях

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

Пример с |:

replace: function (suggestion) {
  const parts = this.input.value.split("|");

  parts[parts.length - 1] = suggestion;

  this.input.value = parts.join("|");
}

Такая модель часто используется в:

  • системах фильтрации
  • поисковых запросах
  • конфигурационных строках

Обработка пустого фрагмента ввода

Особый случай — когда пользователь не ввёл активный фрагмент, но вызвал список подсказок.

replace: function (suggestion) {
  if (!this.input.value.trim()) {
    this.input.value = suggestion;
    return;
  }

  const parts = this.input.value.split(" ");
  parts[parts.length - 1] = suggestion;

  this.input.value = parts.join(" ");
}

Это позволяет избежать некорректной вставки в пустое поле и сохранить предсказуемое поведение интерфейса.