Миграция с других библиотек

Общие принципы миграции

Переход с альтернативных решений для кастомизации <select>-элементов на Choices.js требует учета различий в архитектуре, API и модели управления состоянием. Большинство библиотек для работы с выпадающими списками делятся на две категории: манипуляция DOM напрямую и абстракция над состоянием с внутренним хранилищем данных. Choices.js относится ко второй группе, где DOM является лишь представлением состояния экземпляра.

Ключевые аспекты, влияющие на миграцию:

  • отказ от прямого управления <option> в пользу API экземпляра;
  • единая модель данных для одиночного и множественного выбора;
  • декларативное управление состоянием через методы класса;
  • отсутствие глобальных зависимостей и минимальное вмешательство в DOM;
  • строгая синхронизация состояния между UI и внутренним массивом элементов.

Миграция с нативного <select>

Нативный <select> часто используется как базовая реализация, поверх которой накладывается кастомизация.

Базовая структура до миграции

<sel ect id="city">
  <option value="msk">Москва</option>
  <option value="spb">Санкт-Петербург</option>
  <option value="ekb">Екатеринбург</option>
</select>

Инициализация Choices.js

import Choices fr om 'choices.js';

const select = document.getElementById('city');

const choices = new Choices(select, {
  searchEnabled: true,
  shouldSort: false
});

Особенности перехода

Нативное управление:

select.value = 'spb';

Choices.js:

choices.setChoiceByValue('spb');

Добавление опций:

select.add(new Option('Казань', 'kzn'));

Choices.js:

choices.setChoices([
  { value: 'kzn', label: 'Казань' }
], 'value', 'label', false);

Удаление элементов полностью переносится в API экземпляра:

choices.removeActiveItems();
choices.clearChoices();

Миграция с Select2

Select2 широко использует jQuery и событийную модель, что делает переход на Choices.js концептуально заметным.

Инициализация Select2 (до миграции)

$('#city').select2({
  placeholder: 'Выбор города',
  allowClear: true
});

Эквивалент в Choices.js

const element = document.getElementById('city');

const choices = new Choices(element, {
  removeItemButton: true,
  placeholder: true,
  placeholderValue: 'Выбор города'
});

События

Select2:

$('#city').on('change', function () {
  console.log(this.value);
});

Choices.js:

element.addEventListener('change', (event) => {
  console.log(event.target.value);
});

или через внутренние события:

choices.passedElement.element.addEventListener(
  'addItem',
  (event) => console.log(event.detail.value)
);

Управление данными

Select2 использует jQuery API для обновления данных:

$('#city').val('msk').trigger('change');

Choices.js:

choices.setChoiceByValue('msk');

Миграция с Chosen

Chosen является одной из ранних библиотек, ориентированных на UX улучшение <select>.

Базовая инициализация Chosen

$('#city').chosen({
  no_results_text: 'Ничего не найдено'
});

Эквивалент в Choices.js

const choices = new Choices('#city', {
  noResultsText: 'Ничего не найдено'
});

Отличие архитектуры

Chosen:

  • зависит от jQuery;
  • обновляет DOM напрямую;
  • требует ручного обновления при изменении <option>.

Choices.js:

  • хранит состояние внутри экземпляра;
  • синхронизирует DOM автоматически;
  • использует методы API вместо DOM-манипуляций.

Обновление списка:

Chosen:

$('#city').append('<option value="kzn">Казань</option>');
$('#city').trigger('chosen:updated');

Choices.js:

choices.setChoices([
  { value: 'kzn', label: 'Казань' }
], 'value', 'label', false);

Миграция с Tom Select

Tom Select ближе по философии к Choices.js, однако отличается более богатым API и поддержкой сложных сценариев тегирования.

Инициализация Tom Select

new TomSelect('#city', {
  create: true,
  sortField: 'text'
});

Эквивалент Choices.js

const choices = new Choices('#city', {
  duplicateItemsAllowed: false,
  shouldSort: true
});

Создание новых элементов

Tom Select:

control.addOption({ value: 'kzn', text: 'Казань' });

Choices.js:

choices.setChoices([
  { value: 'kzn', label: 'Казань', selected: false }
], 'value', 'label', false);

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

const choices = new Choices('#city', {
  duplicateItemsAllowed: false,
  addItems: true
});

Миграция мультиселекта

Нативный мультиселект

<select id="tags" multiple>
  <option value="js">JavaScript</option>
  <option value="css">CSS</option>
</select>

Установка значений:

document.getElementById('tags').value = ['js', 'css'];

Choices.js

const choices = new Choices('#tags', {
  removeItemButton: true
});

choices.setChoiceByValue(['js', 'css']);

Добавление элементов:

choices.setChoiceByValue('js');
choices.setChoiceByValue('css');

или пакетно:

choices.setChoices([
  { value: 'js', label: 'JavaScript', selected: true },
  { value: 'css', label: 'CSS', selected: true }
], 'value', 'label', false);

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

Во многих библиотеках используется AJAX-загрузка. В Choices.js отсутствует встроенный AJAX-слой, поэтому миграция требует явного подключения логики загрузки данных.

Пример до миграции (Select2-подобная модель)

$('#city').select2({
  ajax: {
    url: '/api/cities',
    processResults: (data) => ({
      results: data.items
    })
  }
});

Эквивалент в Choices.js

const choices = new Choices('#city', {
  searchEnabled: true
});

fetch('/api/cities')
  .then(res => res.json())
  .then(data => {
    choices.setChoices(
      data.items.map(item => ({
        value: item.id,
        label: item.name
      })),
      'value',
      'label',
      false
    );
  });

Миграция кастомного рендера

Некоторые библиотеки позволяют полностью переопределять шаблоны.

Select2 / Chosen подход

Использование template callbacks или HTML в строках.

Choices.js подход

Использование конфигурации callbackOnCreateTemplates:

const choices = new Choices('#city', {
  callbackOnCreateTemplates: function (template) {
    return {
      item: (classNames, data) => {
        return template(`
          <div class="${classNames.item} ${data.highlighted
            ? classNames.highlightedState
            : classNames.itemSelectable}">
            ${data.label}
          </div>
        `);
      }
    };
  }
});

Миграция событийной модели

Разные библиотеки используют разные уровни событийности.

Типовой разрыв модели

  • jQuery-based библиотеки: DOM-события + кастомные события jQuery;
  • Choices.js: нативные DOM-события + внутренние события экземпляра.

Сопоставление событий

Действие Select2 Choices.js
выбор элемента select2:select addItem
удаление select2:unselect removeItem
открытие select2:open showDropdown
закрытие select2:close hideDropdown

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

choices.passedElement.element.addEventListener('addItem', (e) => {
  console.log(e.detail.value);
});

Миграция валидации и ограничений

В старых библиотеках ограничения часто реализуются через DOM или внешнюю логику.

Select2

maximumSelectionLength: 3

Choices.js

const choices = new Choices('#tags', {
  maxItemCount: 3
});

Дополнительные ограничения:

{
  maxItemText: 'Максимум элементов достигнут',
  duplicateItemsAllowed: false
}

Миграция стилизации

Старые библиотеки часто требуют переопределения CSS через глобальные классы.

Choices.js использует собственную систему классов:

  • choices
  • choices__inner
  • choices__item
  • choices__list

При миграции важно:

  • удалить конфликтующие стили jQuery-плагинов;
  • исключить глобальные селекторы select2-*, chosen-*;
  • адаптировать дизайн под BEM-структуру Choices.js.

Миграция очистки и уничтожения экземпляра

Select2

$('#city').select2('destroy');

Choices.js

choices.destroy();

После уничтожения:

  • DOM возвращается к исходному состоянию;
  • восстановление требует повторной инициализации.

Сравнение архитектурных моделей

Характеристика jQuery-библиотеки Choices.js
зависимость jQuery отсутствует
состояние DOM + plugin data внутренний state
обновление через trigger через API
масштабируемость ограниченная высокая
SSR-совместимость низкая выше

Типовые ошибки при миграции

  • попытка продолжать использовать .val() вместо API;
  • смешивание DOM-обновлений и методов Choices.js;
  • отсутствие синхронизации при динамическом изменении данных;
  • повторная инициализация без destroy();
  • использование старых CSS классов библиотек-источников.