Жизненный цикл экземпляра Tom Select состоит из последовательности внутренних этапов: инициализация, подготовка данных, взаимодействие с пользователем, загрузка данных, изменение состояния, обновление интерфейса и уничтожение компонента. На каждом этапе библиотека генерирует события, позволяющие вмешиваться в поведение системы.
События жизненного цикла используются для:
Экземпляр Tom Select предоставляет единый механизм подписки:
const select = new TomSelect('#users');
select.on('initialize', () => {
console.log('Компонент инициализирован');
});
Удаление обработчика:
function onChange(value) {
console.log(value);
}
select.on('change', onChange);
select.off('change', onChange);
Подписка сразу на несколько событий:
select.on('item_add item_remove', () => {
console.log('Список выбранных элементов изменился');
});
Событие initialize вызывается после завершения полной
инициализации компонента.
На этом этапе:
Пример:
new TomSelect('#tags', {
options: [
{ value: 'js', text: 'JavaScript' },
{ value: 'ts', text: 'TypeScript' }
],
onInitialize() {
console.log('Tom Select готов');
}
});
Альтернативный вариант через .on():
const control = new TomSelect('#tags');
control.on('initialize', () => {
console.log('Инициализация завершена');
});
control.on('initialize', () => {
initTooltips();
initAnalytics();
});
control.on('initialize', () => {
control.settings.maxOptions = 500;
});
control.on('initialize', async () => {
const response = await fetch('/api/default-tags');
const tags = await response.json();
tags.forEach(tag => {
control.addItem(tag.id);
});
});
Событие change является одним из центральных событий
библиотеки. Оно вызывается при изменении значения компонента.
control.on('change', (value) => {
console.log('Новое значение:', value);
});
Для одиночного select:
"javascript"
Для multi-select:
["js", "ts", "react"]
Событие вызывается при:
control.on('change', () => {
form.classList.add('modified');
});
control.on('change', async (value) => {
await fetch('/api/save', {
method: 'POST',
body: JSON.stringify({ value })
});
});
countrySelect.on('change', (country) => {
citySelect.clear();
citySelect.load(`/api/cities?country=${country}`);
});
Событие item_add вызывается после добавления элемента в
список выбранных значений.
control.on('item_add', (value, item) => {
console.log('Добавлен:', value);
});
Значение добавленного элемента.
DOM-элемент выбранного пункта.
control.on('item_add', (value, item) => {
item.classList.add('new-item');
});
control.on('item_add', (value, item) => {
item.animate([
{ opacity: 0 },
{ opacity: 1 }
], {
duration: 300
});
});
control.on('item_add', value => {
analytics.track('tag_added', {
tag: value
});
});
control.on('item_add', () => {
if (control.items.length >= 5) {
control.control_input.disabled = true;
}
});
Срабатывает после удаления элемента.
control.on('item_remove', (value, item) => {
console.log('Удалён:', value);
});
control.on('item_remove', value => {
cache.delete(value);
});
control.on('item_remove', () => {
updateCounters();
});
control.on('item_remove', () => {
if (control.items.length < 5) {
control.control_input.disabled = false;
}
});
Вызывается при полной очистке выбранных значений.
control.on('clear', () => {
console.log('Все элементы удалены');
});
item_remove вызывается для каждого удаляемого элемента
отдельно.
clear вызывается один раз после завершения полной
очистки.
control.on('clear', () => {
preview.innerHTML = '';
});
control.on('clear', () => {
appState.filters = {};
});
Срабатывает при добавлении новой опции в список доступных вариантов.
control.on('option_add', value => {
console.log('Опция добавлена:', value);
});
control.on('option_add', value => {
syncOptionWithServer(value);
});
new TomSelect('#skills', {
create: true,
onOptionAdd(value) {
console.log('Создан новый навык:', value);
}
});
Вызывается после удаления опции.
control.on('option_remove', value => {
console.log('Опция удалена:', value);
});
control.on('option_remove', value => {
delete metadata[value];
});
Полная очистка списка опций.
control.on('option_clear', () => {
console.log('Все опции очищены');
});
control.on('option_clear', () => {
loader.show();
});
Срабатывает при открытии выпадающего списка.
control.on('dropdown_open', dropdown => {
console.log('Dropdown открыт');
});
control.on('dropdown_open', () => {
preloadVisibleItems();
});
control.on('dropdown_open', () => {
analytics.track('dropdown_opened');
});
control.on('dropdown_open', async () => {
if (!control.optionsLoaded) {
await loadOptions();
}
});
Срабатывает после закрытия списка.
control.on('dropdown_close', () => {
console.log('Dropdown закрыт');
});
control.on('dropdown_close', () => {
temporaryCache.clear();
});
control.on('dropdown_close', () => {
control.setTextboxValue('');
});
Вызывается при вводе текста пользователем.
control.on('type', str => {
console.log('Поисковая строка:', str);
});
control.on('type', debounce(async query => {
await search(query);
}, 300));
control.on('type', query => {
if (query.length < 3) {
return;
}
loadResults(query);
});
control.on('type', value => {
showSuggestions(value);
});
Событие load вызывается после успешной загрузки
данных.
control.on('load', data => {
console.log('Получено элементов:', data.length);
});
control.on('load', () => {
spinner.hide();
});
control.on('load', options => {
options.forEach(option => {
option.loadedAt = Date.now();
});
});
Вызывается при получении фокуса.
control.on('focus', () => {
console.log('Фокус установлен');
});
control.on('focus', () => {
wrapper.classList.add('focused');
});
control.on('focus', () => {
control.open();
});
Срабатывает при потере фокуса.
control.on('blur', () => {
console.log('Фокус потерян');
});
control.on('blur', () => {
validateField(control.getValue());
});
control.on('blur', () => {
wrapper.classList.remove('focused');
});
Событие уничтожения экземпляра.
control.on('destroy', () => {
console.log('Компонент уничтожен');
});
control.on('destroy', () => {
window.removeEventListener('resize', onResize);
});
control.on('destroy', () => {
cache.clear();
observers.disconnect();
});
control.destroy();
После вызова:
Tom Select поддерживает два подхода:
.on().new TomSelect('#users', {
onChange(value) {
console.log(value);
}
});
const control = new TomSelect('#users');
control.on('change', value => {
console.log(value);
});
Подходит для:
.on()Подходит для:
Понимание порядка жизненного цикла важно при сложной интеграции.
Типичная последовательность:
type
dropdown_open
load
item_add
change
dropdown_close
item_remove
item_remove
item_remove
clear
change
destroy
Одно действие может запускать цепочку событий.
Пример:
control.on('item_add', value => {
if (value === 'admin') {
control.addItem('permissions');
}
});
Цепочка:
item_add(admin)
change
item_add(permissions)
change
let updating = false;
control.on('change', value => {
if (updating) return;
updating = true;
syncData(value);
updating = false;
});
Tom Select не поддерживает пространства имён событий напрямую, однако их можно эмулировать.
const handlers = {
analyticsChange(value) {
analytics.track(value);
}
};
control.on('change', handlers.analyticsChange);
Удаление:
control.off('change', handlers.analyticsChange);
control.on('change', value => {
eventBus.emit('filters.updated', value);
});
control.on('item_add', value => {
store.dispatch({
type: 'ADD_TAG',
payload: value
});
});
control.on('change', value => {
state.selected = value;
render();
});
[
'initialize',
'change',
'item_add',
'item_remove',
'clear',
'dropdown_open',
'dropdown_close',
'type',
'load',
'focus',
'blur',
'destroy'
].forEach(event => {
control.on(event, (...args) => {
console.log(event, args);
});
});
const timeline = [];
function log(event) {
timeline.push({
event,
time: performance.now()
});
}
control.on('change', () => log('change'));
control.on('load', () => log('load'));
control.on('item_add', () => log('item_add'));
Плохой вариант:
control.on('type', query => {
expensiveOperation(query);
});
Лучше использовать debounce:
control.on('type', debounce(query => {
expensiveOperation(query);
}, 300));
Плохой вариант:
control.on('item_add', () => {
renderLayout();
});
Лучше:
const rerender = debounce(renderLayout, 100);
control.on('item_add item_remove', rerender);
Плагины Tom Select активно используют жизненный цикл.
TomSelect.define('logger', function() {
this.on('item_add', value => {
console.log('Добавлен элемент:', value);
});
this.on('destroy', () => {
console.log('Плагин уничтожен');
});
});
Подключение:
new TomSelect('#users', {
plugins: ['logger']
});
control.on('change blur', () => {
saveForm();
});
control.on('dropdown_open', async () => {
if (control.loading) {
return;
}
control.loading = true;
await loadRemoteData();
control.loading = false;
});
control.on('type change blur', () => {
validate();
});
Ошибка:
function init() {
control.on('change', handler);
}
Если init() вызывается повторно, количество обработчиков
растёт.
Решение:
control.off('change', handler);
control.on('change', handler);
Ошибка:
control.on('change', () => {
window.addEventListener('resize', resizeHandler);
});
Обработчики накапливаются.
Правильный вариант:
window.addEventListener('resize', resizeHandler);
control.on('destroy', () => {
window.removeEventListener('resize', resizeHandler);
});
Ошибка:
control.on('change', value => {
control.setValue(value);
});
Правильный вариант:
let syncing = false;
control.on('change', value => {
if (syncing) return;
syncing = true;
control.setValue(value);
syncing = false;
});