Система событий в библиотеке Tom Select представляет собой механизм реагирования на действия пользователя и изменения состояния компонента. Практически каждая операция внутри элемента выбора сопровождается генерацией событий: ввод текста, открытие выпадающего списка, загрузка данных, выбор элемента, удаление значения, потеря фокуса и многие другие.
События позволяют:
Архитектура событий в Tom Select построена по принципу pub/sub — объект генерирует события, а внешний код подписывается на них через обработчики.
on()Основной механизм подписки на события — метод on.
Базовый синтаксис:
const select = new TomSelect('#users');
select.on('change', function(value) {
console.log('Новое значение:', value);
});
Первый аргумент — имя события.
Второй аргумент — callback-функция, вызываемая при наступлении события.
initializeСрабатывает после полной инициализации компонента.
new TomSelect('#users', {
onInitialize() {
console.log('Tom Select инициализирован');
}
});
Альтернативный вариант:
const select = new TomSelect('#users');
select.on('initialize', () => {
console.log('Компонент готов');
});
changeОдно из наиболее используемых событий.
Вызывается при изменении выбранного значения.
select.on('change', (value) => {
console.log(value);
});
Для режима multiple:
<select id="tags" multiple>
select.on('change', (values) => {
console.log(values);
});
Событие вызывается:
В обработчиках часто используется метод getValue().
select.on('change', () => {
console.log(select.getValue());
});
Для одиночного select:
"user_1"
Для multiple:
["user_1", "user_2"]
item_addСрабатывает при добавлении нового элемента.
select.on('item_add', (value, item) => {
console.log('Добавлен:', value);
});
valueЗначение выбранного элемента.
itemDOM-элемент выбранного item.
select.on('item_add', (value, item) => {
item.classList.add('selected-animation');
});
item_removeВызывается после удаления элемента.
select.on('item_remove', (value) => {
console.log('Удалён:', value);
});
clearСрабатывает после очистки всех выбранных значений.
select.on('clear', () => {
console.log('Все значения удалены');
});
Часто используется совместно с кнопками сброса формы.
dropdown_openselect.on('dropdown_open', () => {
console.log('Список открыт');
});
dropdown_closeselect.on('dropdown_close', () => {
console.log('Список закрыт');
});
select.on('dropdown_open', () => {
document.body.classList.add('overlay');
});
select.on('dropdown_close', () => {
document.body.classList.remove('overlay');
});
focusВызывается при получении фокуса.
select.on('focus', () => {
console.log('Фокус установлен');
});
blurСрабатывает при потере фокуса.
select.on('blur', () => {
console.log('Фокус потерян');
});
Событие blur удобно использовать для проверки данных.
select.on('blur', () => {
const value = select.getValue();
if (!value.length) {
select.wrapper.classList.add('error');
}
});
typeСрабатывает при вводе текста пользователем.
select.on('type', (str) => {
console.log('Поиск:', str);
});
select.on('type', (query) => {
if (query.length < 3) {
return;
}
console.log('Отправка AJAX запроса');
});
loadВызывается после загрузки удалённых данных.
select.on('load', (data) => {
console.log('Получено записей:', data.length);
});
load и AJAXnew TomSelect('#users', {
load(query, callback) {
fetch(`/api/users?q=${query}`)
.then(r => r.json())
.then(data => callback(data));
}
});
select.on('load', (data) => {
console.log(data);
});
option_addСрабатывает при добавлении новой option.
select.on('option_add', (value, data) => {
console.log(value);
console.log(data);
});
option_removeselect.on('option_remove', (value) => {
console.log('Опция удалена');
});
option_clearВызывается после очистки списка опций.
select.on('option_clear', () => {
console.log('Список options очищен');
});
optgroup_addСрабатывает при добавлении группы.
select.on('optgroup_add', (id, data) => {
console.log(id);
});
destroyВызывается перед уничтожением экземпляра.
select.on('destroy', () => {
console.log('Экземпляр уничтожен');
});
Событие destroy критически важно при работе с SPA-приложениями.
select.on('destroy', () => {
window.removeEventListener('resize', resizeHandler);
clearInterval(timer);
});
off()Удаляет обработчики событий.
function onChange(value) {
console.log(value);
}
select.on('change', onChange);
select.off('change', onChange);
select.off('change');
trigger()Позволяет вручную инициировать событие.
select.trigger('change', 'admin');
Tom Select поддерживает кастомные события.
select.on('user_loaded', (user) => {
console.log(user);
});
select.trigger('user_loaded', {
id: 10,
name: 'Alex'
});
select.trigger(
'custom_event',
'users',
15,
true
);
select.on(
'custom_event',
(table, id, active) => {
console.log(table);
console.log(id);
console.log(active);
}
);
Tom Select поддерживает два подхода:
new TomSelect('#users', {
onChange(value) {
console.log(value);
}
});
onselect.on('change', handler);
Подходят для:
on()Подходит для:
this в
событияхВо многих callbacks this указывает на экземпляр Tom
Select.
new TomSelect('#users', {
onChange(value) {
console.log(this);
console.log(this.getValue());
}
});
Стрелочные функции не создают собственный this.
new TomSelect('#users', {
onChange: (value) => {
console.log(this);
}
});
Внутри this уже не будет экземпляром Tom Select.
При выборе элемента может происходить следующая последовательность:
dropdown_opentypeitem_addchangedropdown_closeПонимание порядка событий важно при сложной логике интерфейса.
Обработчики могут быть асинхронными.
select.on('change', async (value) => {
const response = await fetch('/save', {
method: 'POST',
body: JSON.stringify({ value })
});
console.log(await response.json());
});
select.on('change', async (value) => {
try {
await saveUser(value);
} catch(error) {
console.error(error);
}
});
const events = [
'change',
'item_add',
'item_remove',
'dropdown_open',
'dropdown_close'
];
events.forEach(event => {
select.on(event, (...args) => {
console.log(event, args);
});
});
select.on('item_add', (value) => {
analytics.track('select_item', {
value
});
});
document.querySelectorAll('.select').forEach(element => {
const instance = new TomSelect(element);
instance.on('change', (value) => {
console.log(element.id, value);
});
});
При программном изменении значения возможно повторное срабатывание
change.
let updating = false;
select.on('change', (value) => {
if (updating) {
return;
}
updating = true;
select.setValue(value);
updating = false;
});
const originalTrigger = select.trigger;
select.trigger = function() {
console.log(arguments);
return originalTrigger.apply(this, arguments);
};
Внутренне Tom Select хранит события в объекте:
console.log(select._events);
Структура:
{
change: [Function],
item_add: [Function]
}
class UserSelectEvents {
constructor(select) {
this.select = select;
this.register();
}
register() {
this.select.on(
'change',
this.onChange.bind(this)
);
this.select.on(
'item_add',
this.onAdd.bind(this)
);
}
onChange(value) {
console.log(value);
}
onAdd(value) {
console.log(value);
}
}
const handlers = {
change(value) {
console.log(value);
},
item_add(value) {
console.log(value);
}
};
Object.entries(handlers).forEach(([event, handler]) => {
select.on(event, handler);
});
При большом количестве обработчиков возможно ухудшение производительности.
Особенно это касается:
type;load;change.function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
select.on('type', debounce((query) => {
console.log(query);
}, 300));
Нежелательно размещать сложную логику прямо внутри callbacks.
Плохой вариант:
select.on('change', async (value) => {
// 200 строк логики
});
Лучший вариант:
select.on('change', handleUserChange);
async function handleUserChange(value) {
await syncUser(value);
updateUI(value);
}
Многие плагины Tom Select генерируют собственные события.
Например:
Поэтому при использовании расширений важно изучать документацию конкретного плагина.
Жизненный цикл обычно выглядит следующим образом:
Понимание жизненного цикла особенно важно при интеграции Tom Select в: