Библиотека Choices.js построена вокруг событийной модели, в которой
каждый значимый пользовательский или программный шаг в работе селекта
отражается через события DOM-элемента. Основной элемент, через который
осуществляется подписка, — исходный <select> или
<input>, переданный в экземпляр Choices.
const element = document.querySelector('#example');
const choices = new Choices(element, {
searchEnabled: true
});
После инициализации экземпляра становится доступен набор событий, позволяющий отслеживать изменения состояния, взаимодействие пользователя и внутренние операции библиотеки. Эти события становятся основой для логирования.
Choices.js генерирует несколько ключевых событий, которые покрывают почти все пользовательские действия:
Срабатывает при добавлении нового значения.
element.addEventListener('addItem', (event) => {
console.log('Добавлен элемент:', event.detail);
});
event.detail обычно содержит:
Фиксирует удаление выбранного элемента.
element.addEventListener('removeItem', (event) => {
console.log('Удалён элемент:', event.detail);
});
Срабатывает при любом изменении состояния выбора.
element.addEventListener('change', (event) => {
console.log('Состояние изменено:', event.detail.value);
});
Отражает выбор элемента из выпадающего списка.
element.addEventListener('choice', (event) => {
console.log('Выбор из списка:', event.detail);
});
Фиксирует ввод поискового запроса.
element.addEventListener('search', (event) => {
console.log('Поисковый запрос:', event.detail.value);
});
Используются для отслеживания открытия и закрытия интерфейса.
element.addEventListener('showDropdown', () => {
console.log('Открыт список');
});
element.addEventListener('hideDropdown', () => {
console.log('Закрыт список');
});
Простое использование console.log быстро становится
недостаточным. При построении аналитики или отладки вводится
структурированный формат событий.
function logEvent(type, payload) {
const log = {
type,
payload,
timestamp: new Date().toISOString(),
source: 'choices.js'
};
console.log(JSON.stringify(log));
}
Применение:
element.addEventListener('addItem', (e) => {
logEvent('addItem', e.detail);
});
Такой подход позволяет:
При наличии нескольких экземпляров Choices.js целесообразно использовать единый слой логирования.
class ChoicesLogger {
constructor(instance, name) {
this.instance = instance;
this.name = name;
this.bindEvents();
}
bindEvents() {
const el = this.instance.passedElement.element;
el.addEventListener('addItem', (e) => this.log('addItem', e.detail));
el.addEventListener('removeItem', (e) => this.log('removeItem', e.detail));
el.addEventListener('search', (e) => this.log('search', e.detail));
el.addEventListener('choice', (e) => this.log('choice', e.detail));
}
log(eventType, data) {
const record = {
source: this.name,
eventType,
data,
time: Date.now()
};
console.log(record);
}
}
Использование:
const choices = new Choices('#select');
new ChoicesLogger(choices, 'country-selector');
Событие search генерируется при каждом вводе символа.
Это создаёт риск избыточного логирования.
Для уменьшения шума применяется буферизация через debounce:
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
Применение к поиску:
const logSearch = debounce((e) => {
console.log('Поиск:', e.detail.value);
}, 300);
element.addEventListener('search', logSearch);
Такой подход:
При использовании асинхронной подгрузки вариантов важно фиксировать не только действия пользователя, но и сетевые события.
const choices = new Choices('#ajax', {
shouldSort: false,
searchEnabled: true
});
Логирование запросов:
element.addEventListener('search', async (e) => {
const query = e.detail.value;
console.log('Запрос к серверу:', query);
const response = await fetch(`/api/search?q=${query}`);
const data = await response.json();
console.log('Ответ сервера:', {
query,
resultsCount: data.length
});
});
Расширенный вариант фиксирует:
Choices.js позволяет отслеживать текущее состояние выбранных значений
через событие change. Это полезно для построения истории
действий.
element.addEventListener('change', (e) => {
const state = {
value: e.detail.value,
activeItems: choices.getValue(true)
};
console.log('Текущее состояние:', state);
});
Такой подход позволяет:
Хотя Choices.js не генерирует большое количество ошибок на уровне событий, нестандартные состояния можно фиксировать вручную.
try {
choices.setValue([{ value: 'test', label: 'Test' }]);
} catch (e) {
console.log('Ошибка установки значения:', {
message: e.message,
stack: e.stack
});
}
Дополнительно возможно отслеживание некорректных пользовательских действий через проверку данных в событиях:
element.addEventListener('addItem', (e) => {
if (!e.detail.value) {
console.warn('Пустое значение добавления');
}
});
Логирование в Choices.js часто разделяется на два режима:
const DEBUG = true;
function debugLog(...args) {
if (DEBUG) console.log(...args);
}
function logToService(event) {
navigator.sendBeacon('/log', JSON.stringify(event));
}
При сложных интерфейсах Choices.js может быть частью общей системы форм. Тогда события агрегируются:
const eventStream = [];
function pushEvent(type, data) {
eventStream.push({
type,
data,
time: performance.now()
});
}
Применение:
element.addEventListener('addItem', (e) => {
pushEvent('addItem', e.detail);
});
element.addEventListener('removeItem', (e) => {
pushEvent('removeItem', e.detail);
});
Это позволяет:
События Choices.js легко проксируются в системы аналитики через универсальный адаптер:
function analyticsAdapter(eventType, data) {
window.analytics.push({
event: eventType,
properties: data
});
}
Подключение:
element.addEventListener('addItem', (e) => {
analyticsAdapter('choices_add_item', e.detail);
});
Такая схема обеспечивает: