Обновление между версиями Choices.js

Обновление версии библиотеки связано не только с получением новых возможностей. Между релизами Choices.js исправляются ошибки рендеринга, меняется архитектура внутреннего состояния, оптимизируется работа поиска, улучшается поддержка мобильных браузеров и устраняются проблемы совместимости с современными сборщиками.

На практике обновление обычно требуется в следующих случаях:

  • переход проекта на новые версии Webpack, Vite или Rollup;
  • обновление Bootstrap или Tailwind;
  • необходимость работы с большими списками элементов;
  • исправление проблем с памятью;
  • улучшение accessibility;
  • устранение конфликтов со старыми polyfill;
  • поддержка ESM-модулей;
  • отказ от устаревших API.

Анализ текущей версии

Перед обновлением необходимо определить используемую версию библиотеки.

Проверка через package.json

{
  "dependencies": {
    "choices.js": "^9.0.1"
  }
}

Проверка через npm

npm list choices.js

Проверка в браузере

console.log(Choices);

В старых версиях объект библиотеки мог экспортироваться иначе, особенно при использовании UMD-сборки.


Основные отличия между версиями

Изменения API

В разных версиях менялись:

  • названия параметров;
  • поведение событий;
  • структура CSS-классов;
  • экспорт модулей;
  • методы управления экземпляром;
  • правила работы с shadowRoot;
  • логика поиска;
  • шаблонизация.

Даже минорное обновление иногда приводит к несовместимости интерфейсов.


Переход с Choices.js 8.x на 9.x

Изменение импорта

Старый вариант

import Choices from 'choices.js/assets/scripts/choices';

Новый вариант

import Choices from 'choices.js';

Новые версии предоставляют корректный entry point для ESM.


Изменение подключения стилей

Старый способ

import 'choices.js/assets/styles/css/choices.css';

Новый способ

import 'choices.js/public/assets/styles/choices.css';

В некоторых версиях структура каталогов изменялась несколько раз, поэтому после обновления важно проверить путь внутри node_modules.


Изменения структуры CSS

Старые классы

<div class="choices__inner"></div>

Возможные изменения

В новых релизах могли появляться:

<div class="choices__list choices__list--single"></div>

или дополнительные служебные контейнеры.


Риски при обновлении

Если проект содержит кастомные стили:

.choices__inner {
  height: 40px;
}

то после обновления могут возникнуть:

  • сломанная высота;
  • неправильное позиционирование dropdown;
  • исчезновение placeholder;
  • конфликт flex-layout;
  • обрезка текста.

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

После обновления необходимо проверить:

  • размеры контейнеров;
  • адаптивность;
  • hover-состояния;
  • dark mode;
  • disabled-состояния;
  • RTL-режим;
  • стили мультиселекта;
  • overflow длинных элементов.

Изменения событий

Старые события

В ранних версиях события могли передавать разные структуры объекта.

element.addEventListener('addItem', (event) => {
  console.log(event.detail);
});

Изменение структуры detail

Старые версии:

event.detail.value

Новые версии:

event.detail.label
event.detail.value
event.detail.customProperties

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


Проверка пользовательских обработчиков

Потенциально проблемный код

element.addEventListener('choice', function(event) {
  send(event.detail.choice.id);
});

После обновления:

  • choice может отсутствовать;
  • изменяется вложенность объекта;
  • меняется тип данных;
  • поля становятся необязательными.

Изменения методов экземпляра

Методы, изменившие поведение

Особенно часто изменения касались:

  • setChoices
  • clearStore
  • clearChoices
  • setValue
  • removeActiveItems

Пример несовместимости

Старый код

choices.setChoices(data, 'value', 'label', true);

Новый код

choices.setChoices(data, 'value', 'label', false);

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

Это приводило к неожиданной очистке списка.


Изменения асинхронного API

Старый подход

choices.setChoices(async () => {
  return fetchData();
});

Новый подход

choices.setChoices(async () => {
  const data = await fetchData();
  return data;
});

Некоторые версии меняли обработку Promise и внутренний lifecycle.


Проблемы после обновления

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

Частая проблема:

choices.setChoices(newData);
choices.setChoices(newData);

После обновления библиотека могла перестать автоматически очищать store.


Решение

choices.clearChoices();
choices.setChoices(newData);

Изменения destroy()

Старое поведение

choices.destroy();

Экземпляр удалял DOM-обёртку частично.


Новое поведение

В новых версиях:

  • удаляются слушатели;
  • очищается store;
  • восстанавливается original select;
  • снимаются mutation observer.

Проблемы повторной инициализации

Ошибочный подход

new Choices(element);
new Choices(element);

После обновления подобный код чаще приводит к:

  • утечкам памяти;
  • дублированию dropdown;
  • множественным событиям;
  • поломанному поиску.

Безопасная схема

if (instance) {
  instance.destroy();
}

instance = new Choices(element);

Обновление в проектах с jQuery

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

Потенциальная проблема

$('#select').html(options);

После обновления Choices.js внутренний store может не синхронизироваться с DOM.


Корректный вариант

choices.clearChoices();
choices.setChoices(data, 'value', 'label', true);

Изменения поиска

Старый механизм

Поиск выполнялся проще и медленнее.


Новый механизм

Новые версии:

  • используют оптимизированный matcher;
  • поддерживают fuzzy-search;
  • уменьшают количество reflow;
  • ускоряют фильтрацию больших списков.

Проверка производительности после обновления

Особенно важно тестировать:

  • списки на 1000+ элементов;
  • мультиселекты;
  • async loading;
  • частые обновления;
  • динамические формы.

Изменения placeholder

Старое поведение

placeholder: true

Новое поведение

В некоторых версиях потребовалось:

placeholder: true,
placeholderValue: 'Выберите значение'

Проблемы с пустыми option

Старый HTML

<option value=""></option>

После обновления

Пустой option мог:

  • отображаться как обычный элемент;
  • исчезать;
  • ломать placeholder;
  • конфликтовать с required.

Изменения required-полей

После обновления возможны ситуации:

  • браузер считает select пустым;
  • form validation перестаёт работать;
  • required игнорируется.

Проверка формы

form.checkValidity();

Необходимо тестировать нативную HTML-валидацию отдельно.


Изменения сборки библиотеки

Старые версии

Использовали:

  • UMD;
  • CommonJS;
  • глобальный объект.

Новые версии

Основной акцент делается на:

  • ESM;
  • tree shaking;
  • современные bundler;
  • минимизацию размера пакета.

Проблемы Webpack

Ошибка

Module parse failed

Причина

Старый конфиг Webpack не обрабатывает современные модули.


Решение

Обновление:

  • Babel;
  • webpack loaders;
  • target environment.

Обновление Vite-проектов

В Vite обычно проблемы связаны с:

  • импортом CSS;
  • SSR;
  • dynamic import;
  • hydration.

Проверка SSR

Choices.js использует DOM API.

Поэтому код:

new Choices(element);

не должен выполняться на сервере.


Безопасный вариант

if (typeof window !== 'undefined') {
  new Choices(element);
}

Обновление в React

Типичная проблема

React повторно рендерит select.

Choices.js при этом:

  • теряет состояние;
  • создаёт лишние контейнеры;
  • ломает controlled component.

Правильная интеграция

useEffect(() => {
  const instance = new Choices(ref.current);

  return () => {
    instance.destroy();
  };
}, []);

Изменения в Vue

Во Vue особенно важно:

  • уничтожать экземпляр в beforeUnmount;
  • избегать повторной инициализации;
  • синхронизировать reactive state.

Обновление Angular-проектов

После обновления могут появляться:

  • проблемы Zone.js;
  • лишние change detection;
  • ошибки lifecycle.

Рекомендуемый подход

ngAfterViewInit() {
  this.choices = new Choices(this.element.nativeElement);
}

ngOnDestroy() {
  this.choices.destroy();
}

Изменения accessibility

Новые версии улучшали:

  • ARIA-атрибуты;
  • keyboard navigation;
  • screen reader compatibility.

Что необходимо проверить

Навигация клавиатурой

Проверяются:

  • Tab;
  • Enter;
  • Escape;
  • Arrow Up;
  • Arrow Down;
  • Backspace.

Screen reader

После обновления необходимо тестировать:

  • NVDA;
  • VoiceOver;
  • JAWS.

Изменения шаблонов

Старые custom templates

callbackOnCreateTemplates: function(template) {
  return {};
}

После обновления

Могли измениться:

  • аргументы;
  • структура HTML;
  • сигнатура callback;
  • internal class names.

Проверка XSS-защиты

В новых версиях могли усиливаться механизмы sanitization.


Проблемный код

allowHTML: true

После обновления:

  • HTML может экранироваться;
  • шаблоны работают иначе;
  • custom renderer ломается.

Стратегии безопасного обновления

Пошаговое обновление

Плохой подход:

npm install choices.js@latest

Предпочтительный вариант

Последовательное обновление:

8.x → 9.x → 10.x → 11.x

Так проще выявлять несовместимости.


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

При обновлении необходимо анализировать:

  • breaking changes;
  • deprecated API;
  • renamed options;
  • migration notes;
  • known issues.

Создание тестового стенда

Перед обновлением желательно подготовить:

  • страницу со всеми типами select;
  • async-загрузку;
  • формы;
  • mobile layout;
  • кастомные стили;
  • validation;
  • search scenarios.

Автоматизированные тесты

Проверка выбора

expect(select.value).toBe('2');

Проверка событий

expect(handler).toHaveBeenCalled();

Проверка DOM

expect(document.querySelector('.choices')).not.toBeNull();

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

Ошибка двойного destroy

instance.destroy();
instance.destroy();

После обновления некоторые версии перестали игнорировать повторный вызов.


Ошибка работы с DOM напрямую

select.innerHTML = '';

Choices.js может потерять внутреннее состояние.


Ошибка смешивания API

choices.setChoices(...);
select.appendChild(...);

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


Проверка браузерной совместимости

После обновления важно протестировать:

  • Chrome;
  • Firefox;
  • Safari;
  • мобильный Safari;
  • Android WebView.

Проблемы мобильных устройств

На мобильных устройствах после обновления возможны:

  • неправильный scroll dropdown;
  • проблемы focus;
  • перекрытие клавиатурой;
  • потеря tap-событий.

Проверка памяти

Choices.js активно работает с DOM и событиями.

После обновления следует проверять:

  • detached nodes;
  • listeners;
  • mutation observers;
  • повторную инициализацию.

Проверка через DevTools

Особенно полезны:

  • Memory Snapshot;
  • Event Listener Breakpoints;
  • Performance Timeline.

Использование feature flags

В крупных проектах обновление часто внедряется постепенно.

Пример

const useNewChoices = true;

Переключение реализации

if (useNewChoices) {
  initNewChoices();
} else {
  initOldChoices();
}

Параллельное тестирование версий

Иногда удобно временно запускать две версии:

choices-v9
choices-v11

Это помогает сравнить:

  • поведение;
  • производительность;
  • DOM;
  • события.

Контроль deprecated API

Во многих версиях старые методы сначала помечаются как deprecated.


Опасный сценарий

Метод работает:

choices.someOldMethod();

но в следующем major-релизе полностью удаляется.


Рекомендации по миграции крупных проектов

Последовательность действий

  1. Анализ changelog.
  2. Обновление зависимостей.
  3. Проверка импортов.
  4. Проверка CSS.
  5. Проверка событий.
  6. Проверка destroy/init.
  7. Тестирование форм.
  8. Проверка мобильных устройств.
  9. Проверка accessibility.
  10. Нагрузочное тестирование.

Минимизация рисков

Наиболее стабильный подход:

  • обновлять постепенно;
  • фиксировать версии;
  • использовать integration tests;
  • избегать прямых DOM-манипуляций;
  • не смешивать старый и новый API;
  • проверять lifecycle компонентов;
  • централизовать инициализацию Choices.js.