Механизм расширения в Choices.js построен вокруг концепции подключаемых модулей, которые позволяют вмешиваться в жизненный цикл компонента, модифицировать поведение UI, обрабатывать события и расширять функциональность без изменения исходного кода библиотеки.
Плагин в Choices.js представляет собой объект или функцию,
возвращающую объект с набором хуков (hooks), которые вызываются в строго
определённых точках жизненного цикла экземпляра Choices.
Основная идея заключается в инверсии управления: библиотека
предоставляет точки расширения, а плагин внедряет туда собственную
логику.
Типовой плагин реализуется как функция, принимающая экземпляр
choices и возвращающая объект с методами-хуками.
function MyPlugin(choices) {
return {
init() {
// инициализация плагина
},
onCreate() {
// вызывается после создания экземпляра
},
onChoiceAdd(choice) {
// вызывается при добавлении элемента
},
onChoiceRemove(choice) {
// вызывается при удалении элемента
},
destroy() {
// очистка ресурсов
}
};
}
Каждый метод привязывается к внутренним событиям экземпляра и вызывается автоматически при наступлении соответствующего состояния.
Choices.js не требует глобальной регистрации плагинов. Подключение происходит через опции конструктора.
const instance = new Choices('#select', {
plugins: [MyPlugin]
});
При инициализации библиотеки каждый плагин вызывается с текущим
экземпляром Choices, после чего его методы регистрируются в
внутреннем диспетчере событий.
Choicesinit() у каждого плагинаonCreateПорядок важен, так как плагины могут зависеть от уже созданной структуры DOM.
Каждый плагин взаимодействует с объектом choices,
который содержит:
config — конфигурация экземпляраstore — состояние выбранных значенийcontainer — DOM-контейнерinput — исходный элемент <select>
или <input>triggerEvent — метод генерации событийВызывается один раз при подключении плагина. Используется для:
init() {
this.choices.container.classList.add('plugin-enabled');
}
Срабатывает после полной инициализации экземпляра.
onCreate() {
this.choices._storePluginData = {};
}
Вызывается при добавлении нового выбранного элемента.
onChoiceAdd(choice) {
console.log('Добавлен выбор:', choice);
}
Вызывается при удалении значения из списка выбранных элементов.
onChoiceRemove(choice) {
console.log('Удалён выбор:', choice);
}
Обязательный метод очистки. Используется для удаления обработчиков событий и временных данных.
destroy() {
this.choices.container.classList.remove('plugin-enabled');
}
Choices.js использует внутреннюю систему событий, основанную на
паттерне observer. Плагины могут подписываться на события через
triggerEvent или напрямую через внутренний emitter.
function MyPlugin(choices) {
function handleChange(event) {
console.log('Изменение:', event.detail);
}
return {
init() {
choices.container.addEventListener('change', handleChange);
},
destroy() {
choices.container.removeEventListener('change', handleChange);
}
};
}
Наиболее значимые события:
addItemremoveItemhighlightItemshowDropdownhideDropdownsearchПлагины могут использовать эти события для синхронизации состояния.
Одной из ключевых возможностей является расширение интерфейса без изменения ядра.
function AddBadgePlugin(choices) {
let badge;
return {
init() {
badge = document.createElement('div');
badge.className = 'choices__badge';
badge.textContent = 'Custom';
choices.container.appendChild(badge);
},
destroy() {
if (badge) {
badge.remove();
}
}
};
}
init() {
const dropdown = this.choices.dropdown.element;
const footer = document.createElement('div');
footer.className = 'custom-footer';
footer.textContent = 'Дополнительная информация';
dropdown.appendChild(footer);
}
Плагин имеет доступ к внутреннему хранилищу выбранных значений через
store.
function CounterPlugin(choices) {
let counter;
return {
init() {
counter = document.createElement('span');
counter.className = 'choices-counter';
choices.container.appendChild(counter);
this.update();
},
onChoiceAdd() {
this.update();
},
onChoiceRemove() {
this.update();
},
update() {
const count = choices.store.activeItems.length;
counter.textContent = `Выбрано: ${count}`;
},
destroy() {
counter.remove();
}
};
}
Choices.js допускает одновременную работу нескольких плагинов, однако порядок их подключения может влиять на результат.
const instance = new Choices('#select', {
plugins: [PluginA, PluginB, PluginC]
});
Типовые проблемы:
Для предотвращения конфликтов используется:
Плагин может расширять конфигурацию экземпляра, добавляя собственные параметры.
function ConfigPlugin(choices) {
const config = choices.config.pluginOptions?.configPlugin || {};
return {
init() {
if (config.enableLogging) {
console.log('Логирование включено');
}
}
};
}
Использование:
const instance = new Choices('#select', {
plugins: [ConfigPlugin],
pluginOptions: {
configPlugin: {
enableLogging: true
}
}
});
Плагины могут работать асинхронно, например, при загрузке данных или удалённых ресурсов.
function AsyncPlugin(choices) {
return {
async init() {
const response = await fetch('/api/data');
const data = await response.json();
data.forEach(item => {
choices.setChoices([
{ value: item.id, label: item.name }
], 'value', 'label', true);
});
}
};
}
При разработке плагинов важно учитывать внутренние ограничения:
_-поля)destroy()Нарушение этих правил приводит к утечкам памяти и нестабильному поведению экземпляра.
Изоляция логики через замыкания:
function Plugin() {
let state = {};
return function(choices) {
return {
init() {
state.initialized = true;
}
};
};
}
Позволяет параметризовать поведение:
function createPlugin(options) {
return function(choices) {
return {
init() {
if (options.debug) {
console.log('Debug mode');
}
}
};
};
}
Расширение существующего поведения:
function decoratorPlugin(choices) {
const originalAddItem = choices._addItem;
choices._addItem = function() {
console.log('Before add');
const result = originalAddItem.apply(this, arguments);
console.log('After add');
return result;
};
}
Тестирование строится вокруг имитации экземпляра
Choices.
const mockChoices = {
container: document.createElement('div'),
store: { activeItems: [] },
triggerEvent: () => {}
};
const plugin = MyPlugin(mockChoices);
plugin.init();
Проверяются:
destroy()Эти ошибки приводят к снижению предсказуемости поведения и усложняют интеграцию в большие проекты.