Интеграция Choices.js в Svelte опирается на жизненный цикл компонента
и работу с DOM-элементами через bind:this. Поскольку Svelte
выполняет реактивное обновление состояния, а Choices.js напрямую
управляет DOM, ключевой задачей становится изоляция управления
библиотекой в пределах onMount и корректное освобождение
ресурсов при размонтировании компонента.
Базовая структура подключения начинается с создания ссылки на
<select> или <input> элемент:
<script>
import { onMount, onDestroy } from 'svelte';
import Choices from 'choices.js';
let selectElement;
let choicesInstance;
const options = [
{ value: 'js', label: 'JavaScript' },
{ value: 'ts', label: 'TypeScript' },
{ value: 'py', label: 'Python' }
];
</script>
<sel ect bind:this={selectElement}>
<option value="">Выбор языка</option>
{#each options as opt}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
Инициализация Choices.js выполняется после монтирования компонента, когда DOM-узел уже существует:
<script>
import { onMount, onDestroy } fr om 'svelte';
import Choices from 'choices.js';
let selectElement;
let choicesInstance;
const options = [
{ value: 'js', label: 'JavaScript' },
{ value: 'ts', label: 'TypeScript' },
{ value: 'py', label: 'Python' }
];
onMount(() => {
choicesInstance = new Choices(selectElement, {
searchEnabled: true,
shouldSort: false,
itemSelectText: '',
placeholderValue: 'Выберите язык'
});
});
onDestroy(() => {
if (choicesInstance) {
choicesInstance.destroy();
}
});
</script>
Svelte управляет состоянием через реактивные переменные, тогда как Choices.js хранит собственное внутреннее состояние. Для синхронизации используется обновление через API экземпляра.
Реактивное изменение списка опций:
<script>
import { onMount } from 'svelte';
import Choices from 'choices.js';
let selectElement;
let choicesInstance;
let languages = [
{ value: 'js', label: 'JavaScript' },
{ value: 'ts', label: 'TypeScript' }
];
onMount(() => {
choicesInstance = new Choices(selectElement, {
shouldSort: false
});
});
function addLanguage() {
languages = [
...languages,
{ value: 'py', label: 'Python' }
];
choicesInstance.clearChoices();
choicesInstance.setChoices(languages, 'value', 'label', true);
}
</script>
<sel ect bind:this={selectElement}>
{#each languages as lang}
<option value={lang.value}>{lang.label}</option>
{/each}
</select>
<button on:click={addLanguage}>Добавить язык</button>
Метод setChoices с четвёртым аргументом
true позволяет полностью перезаписать список, обеспечивая
согласованность между состоянием Svelte и внутренним состоянием
Choices.js.
Choices.js не заменяет механизм привязки Svelte, поэтому для отслеживания выбранного значения используется обработка событий DOM или подписка на события библиотеки.
Обработка изменения выбора:
<script>
import { onMount } fr om 'svelte';
import Choices from 'choices.js';
let selectElement;
let choicesInstance;
let selectedValue = '';
onMount(() => {
choicesInstance = new Choices(selectElement, {
shouldSort: false
});
selectElement.addEventListener('change', (event) => {
selectedValue = event.target.value;
});
});
</script>
<p>Выбрано: {selectedValue}</p>
<sel ect bind:this={selectElement}>
<option value="js">JavaScript</option>
<option value="ts">TypeScript</option>
</select>
Альтернативный подход использует API Choices.js:
choicesInstance.passedElement.element.addEventListener(
'change',
(event) => {
selectedValue = event.detail.value;
}
);
При включении removeItemButton и
maxItemCount компонент Svelte остаётся источником данных, а
Choices.js отвечает за UI-слой.
<script>
import { onMount } fr om 'svelte';
import Choices from 'choices.js';
let selectElement;
let choicesInstance;
let selected = [];
onMount(() => {
choicesInstance = new Choices(selectElement, {
removeItemButton: true,
maxItemCount: 3,
shouldSort: false
});
selectElement.addEventListener('change', (e) => {
selected = Array.from(selectElement.selectedOptions).map(
(opt) => opt.value
);
});
});
</script>
<sel ect bind:this={selectElement} multiple>
<option value="js">JavaScript</option>
<option value="ts">TypeScript</option>
<option value="py">Python</option>
</select>
<p>Выбранные значения: {selected.join(', ')}</p>
При использовании writable store синхронизация
становится централизованной. Choices.js обновляется при изменении
store.
<script>
import { onMount } fr om 'svelte';
import { writable } from 'svelte/store';
import Choices from 'choices.js';
let selectElement;
let choicesInstance;
const optionsStore = writable([
{ value: 'js', label: 'JavaScript' }
]);
let options = [];
optionsStore.subscribe((value) => {
options = value;
if (choicesInstance) {
choicesInstance.setChoices(options, 'value', 'label', true);
}
});
onMount(() => {
choicesInstance = new Choices(selectElement, {
shouldSort: false
});
});
function addOption() {
optionsStore.update((prev) => [
...prev,
{ value: 'ts', label: 'TypeScript' }
]);
}
</script>
<sel ect bind:this={selectElement}>
{#each options as opt}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
<button on:click={addOption}>Добавить</button>
Choices.js поддерживает ленивую подгрузку через
setChoices, что удобно при запросах к API.
<script>
import { onMount } fr om 'svelte';
import Choices from 'choices.js';
let selectElement;
let choicesInstance;
async function fetchOptions() {
return [
{ value: 'ru', label: 'Russian' },
{ value: 'en', label: 'English' },
{ value: 'de', label: 'German' }
];
}
onMount(async () => {
choicesInstance = new Choices(selectElement, {
searchEnabled: true
});
const data = await fetchOptions();
choicesInstance.setChoices(data, 'value', 'label', true);
});
</script>
<sel ect bind:this={selectElement}></select>
Особенность интеграции заключается в необходимости строгого удаления
экземпляра при размонтировании компонента. Игнорирование
destroy() приводит к утечкам памяти и дублированию
обработчиков событий.
onDestroy(() => {
if (choicesInstance) {
choicesInstance.destroy();
choicesInstance = null;
}
});
При повторной инициализации компонента важно гарантировать отсутствие старых подписок и обработчиков.
Choices.js позволяет переопределять отображение элементов через
конфигурацию callbackOnCreateTemplates, что может быть
адаптировано под Svelte-компоненты, если требуется унификация
UI-стиля.
choicesInstance = new Choices(selectElement, {
callbackOnCreateTemplates: function (template) {
return {
item: (classNames, data) => {
return template(`
<div class="${classNames.item} ${data.highlighted
? classNames.highlightedState
: classNames.itemSelectable}">
${data.label}
</div>
`);
}
};
}
});
При использовании SvelteKit и серверного рендеринга необходимо
учитывать, что Choices.js работает исключительно в браузере. Любая
инициализация должна быть ограничена onMount, который не
выполняется на сервере.
<script>
import { browser } fr om '$app/environment';
import { onMount } from 'svelte';
import Choices from 'choices.js';
let selectElement;
onMount(() => {
if (!browser) return;
const instance = new Choices(selectElement, {
shouldSort: false
});
return () => instance.destroy();
});
</script>
Такой подход исключает ошибки гидратации и обращения к
window или document на сервере.
При сложных интерфейсах Choices.js выступает как слой представления, тогда как Svelte хранит истинное состояние. Любое изменение должно проходить через единый источник данных, иначе возникает рассинхронизация DOM и логики приложения.
Модель взаимодействия:
Такое разделение позволяет поддерживать предсказуемость состояния при динамических изменениях интерфейса и асинхронных обновлениях данных.