Нативный элемент <select> в HTML остаётся базовым
способом работы с выпадающими списками, однако его поведение и внешний
вид сильно ограничены. Основные проблемы, которые подталкивают к
миграции на Choices.js:
Choices.js решает эти задачи, предоставляя управляемый, расширяемый
компонент поверх стандартного <select> или
<input>.
Миграция начинается с понимания ключевого принципа: библиотека не заменяет данные, а оборачивает существующий элемент и синхронизирует состояние.
Исходный HTML:
<sel ect id="city">
<option value="almaty">Almaty</option>
<option value="astana">Astana</option>
<option value="shymkent">Shymkent</option>
</select>
Инициализация Choices.js:
import Choices fr om 'choices.js';
const element = document.getElementById('city');
const cityChoices = new Choices(element, {
searchEnabled: true,
shouldSort: false
});
После инициализации:
<select> скрывается;<select>.Перед миграцией важно определить, как именно используется
<select>:
optgroup;Choices.js поддерживает большинство сценариев, но поведение нужно перенести явно, иначе возникают расхождения между старой и новой логикой.
Стандартный сценарий — одиночный выбор.
<select id="language">
<option value="ru">Русский</option>
<option value="en">English</option>
</select>
const language = new Choices('#language', {
searchEnabled: false,
itemSelectText: ''
});
searchEnabled: false имитирует поведение обычного
select;itemSelectText убирает подсказки интерфейса;<select>.Множественный выбор — один из наиболее частых кейсов, где native
<select> быстро становится неудобным.
<select id="tags" multiple>
<option value="js">JavaScript</option>
<option value="css">CSS</option>
<option value="html">HTML</option>
</select>
const tags = new Choices('#tags', {
removeItemButton: true,
shouldSort: false
});
selected.При миграции часто возникает необходимость заменить статические
<option> на динамическую загрузку.
const cities = new Choices('#city', {
shouldSort: false
});
cities.setChoices([
{ value: 'almaty', label: 'Almaty' },
{ value: 'astana', label: 'Astana' }
], 'value', 'label', true);
Одним из критических моментов миграции является сохранение совместимости с backend-логикой.
Choices.js не меняет принцип отправки формы:
<select>;Однако при кастомных сценариях важно учитывать:
В нативном <select> часто используются события
change напрямую на элементе.
document.getElementById('city').addEventListener('change', (e) => {
console.log(e.target.value);
});
После миграции:
const city = new Choices('#city');
document.querySelector('#city').addEventListener('change', (e) => {
console.log(e.target.value);
});
Choices.js также предоставляет собственные события:
city.passedElement.element.addEventListener(
'addItem',
(event) => {
console.log(event.detail.value);
}
);
addItem — добавление значения;removeItem — удаление;change — изменение состояния;search — ввод в поиске.Группировка опций в нативном <select>:
<select id="cars">
<optgroup label="German">
<option value="bmw">BMW</option>
<option value="audi">Audi</option>
</optgroup>
</select>
Choices.js поддерживает аналогичную структуру через
choices:
const cars = new Choices('#cars', {
shouldSort: false
});
cars.setChoices([
{
label: 'German',
id: 'german',
disabled: false,
choices: [
{ value: 'bmw', label: 'BMW' },
{ value: 'audi', label: 'Audi' }
]
}
], 'value', 'label', true);
Возникает при прямом изменении DOM:
document.querySelector('#city').innerHTML = '';
Choices.js не отслеживает такие изменения автоматически. Нужно использовать API:
city.clearStore();
Частая ошибка в SPA:
new Choices('#city');
new Choices('#city');
Результат — конфликт экземпляров и утечка памяти.
Решение:
if (!element.dataset.choicesInit) {
new Choices(element);
element.dataset.choicesInit = true;
}
Нативный <select> допускает простые строки, но
Choices.js требует структурированных объектов при динамической
загрузке.
Ошибочный подход:
setChoices(['Almaty', 'Astana']);
Корректный:
setChoices([
{ value: 'almaty', label: 'Almaty' },
{ value: 'astana', label: 'Astana' }
]);
В крупных проектах редко происходит полная замена сразу. Чаще применяется поэтапный подход:
<select> в части формы;useEffect(() => {
const instance = new Choices(ref.current);
return () => instance.destroy();
}, []);
mounted() {
this.choices = new Choices(this.$refs.select);
},
beforeUnmount() {
this.choices.destroy();
}
ngAfterViewInit() {
this.choices = new Choices(this.select.nativeElement);
}
ngOnDestroy() {
this.choices.destroy();
}
При миграции важно учитывать жизненный цикл компонента:
city.destroy();
Это:
<select>;Переход от нативного <select> к Choices.js требует
смены подхода:
Главное изменение заключается не в синтаксисе, а в переносе логики управления выбором из HTML в JavaScript-слой.