Библиотека Tom Select построена вокруг событийной архитектуры. Практически любое действие пользователя — ввод текста, выбор элемента, удаление значения, открытие выпадающего списка, потеря фокуса — генерирует событие, на которое можно подписаться.
События позволяют:
Tom Select использует собственную систему событий, работающую через методы:
tomselect.on(event, handler);
tomselect.off(event, handler);
tomselect.trigger(event, ...args);
Экземпляр компонента обычно получают следующим образом:
const select = new TomSelect('#users');
После этого становится доступна подписка на пользовательские события.
Базовая подписка выглядит так:
select.on('change', (value) => {
console.log('Новое значение:', value);
});
Первый аргумент — имя события.
Второй аргумент — обработчик.
Для удаления используется off.
function onChange(value) {
console.log(value);
}
select.on('change', onChange);
select.off('change', onChange);
Если не удалять старые обработчики при повторной инициализации компонентов, возможно накопление подписок и утечки памяти.
Метод trigger запускает событие вручную.
select.trigger('custom:update', {
status: 'ok'
});
Это особенно полезно при интеграции нескольких компонентов.
Событие initialize вызывается после полной инициализации
компонента.
select.on('initialize', () => {
console.log('Компонент готов');
});
Типичные сценарии:
Пример:
select.on('initialize', () => {
select.focus();
});
Одно из самых важных событий.
Вызывается при изменении выбранного значения.
select.on('change', (value) => {
console.log('Выбрано:', value);
});
Для multiple-режима значение может быть массивом.
const select = new TomSelect('#skills', {
maxItems: null
});
select.on('change', (values) => {
console.log(values);
});
select.on('change', async (value) => {
await fetch('/api/profile', {
method: 'POST',
body: JSON.stringify({ country: value })
});
});
select.on('change', (value) => {
if (!value) {
select.control.classList.add('error');
return;
}
select.control.classList.remove('error');
});
Срабатывает после добавления элемента.
select.on('item_add', (value, item) => {
console.log(value);
});
Аргументы:
| Аргумент | Описание |
|---|---|
| value | значение элемента |
| item | DOM-узел выбранного элемента |
select.on('item_add', (value, item) => {
item.classList.add('selected-item');
});
select.on('item_add', () => {
if (select.items.length >= 5) {
select.disable();
}
});
select.on('item_add', (value) => {
analytics.track('skill_added', {
skill: value
});
});
Вызывается после удаления выбранного элемента.
select.on('item_remove', (value) => {
console.log('Удалено:', value);
});
select.on('item_remove', () => {
if (select.isDisabled && select.items.length < 5) {
select.enable();
}
});
select.on('item_remove', (value) => {
delete cache[value];
});
Срабатывает после полной очистки выбранных значений.
select.on('clear', () => {
console.log('Все элементы удалены');
});
select.on('clear', () => {
document.querySelector('.preview').innerHTML = '';
});
select.on('clear', () => {
select.clearOptions();
loadDefaultOptions();
});
Срабатывает при добавлении новой опции.
select.on('option_add', (value, data) => {
console.log(value, data);
});
select.addOption({
value: 'vue',
text: 'Vue'
});
Событие:
select.on('option_add', (value, data) => {
console.log('Новая опция:', data.text);
});
Вызывается после удаления опции.
select.on('option_remove', (value) => {
console.log('Опция удалена:', value);
});
select.on('option_remove', (value) => {
localCache.delete(value);
});
Полностью очищает список опций.
select.on('option_clear', () => {
console.log('Список очищен');
});
select.on('option_clear', async () => {
const response = await fetch('/api/options');
const data = await response.json();
select.addOptions(data);
});
Срабатывает при открытии выпадающего списка.
select.on('dropdown_open', () => {
console.log('Dropdown открыт');
});
select.on('dropdown_open', async () => {
if (select.loadedSearches.initialized) {
return;
}
const response = await fetch('/api/tags');
const tags = await response.json();
select.addOptions(tags);
select.loadedSearches.initialized = true;
});
select.on('dropdown_open', () => {
document.body.classList.add('dropdown-active');
});
Срабатывает при закрытии списка.
select.on('dropdown_close', () => {
document.body.classList.remove('dropdown-active');
});
select.on('dropdown_close', () => {
tempSearchResults = [];
});
Событие вызывается во время ввода текста пользователем.
select.on('type', (str) => {
console.log(str);
});
select.on('type', async (query) => {
if (query.length < 2) {
return;
}
const response = await fetch(`/api/search?q=${query}`);
const items = await response.json();
select.clearOptions();
select.addOptions(items);
});
Без ограничения количества запросов пользователь может перегрузить сервер.
let timeout;
select.on('type', (query) => {
clearTimeout(timeout);
timeout = setTimeout(async () => {
const response = await fetch(`/api/search?q=${query}`);
const data = await response.json();
select.clearOptions();
select.addOptions(data);
}, 300);
});
Срабатывает после завершения загрузки данных.
select.on('load', (data) => {
console.log(data);
});
select.on('load', () => {
loader.style.display = 'none';
});
select.on('load', (data) => {
resultsCounter.textContent = `Найдено: ${data.length}`;
});
Вызывается при получении фокуса.
select.on('focus', () => {
console.log('Фокус');
});
select.on('focus', async () => {
if (select.optionsLoaded) {
return;
}
await loadOptions();
select.optionsLoaded = true;
});
Срабатывает при потере фокуса.
select.on('blur', () => {
console.log('Потеря фокуса');
});
select.on('blur', () => {
if (select.items.length === 0) {
select.control.classList.add('invalid');
}
});
Вызывается перед уничтожением экземпляра.
select.on('destroy', () => {
console.log('Компонент уничтожен');
});
select.on('destroy', () => {
window.removeEventListener('resize', onResize);
clearInterval(syncTimer);
});
Срабатывает при выборе уже существующего элемента внутри компонента.
select.on('item_select', (item) => {
console.log(item);
});
select.on('item_select', (item) => {
const id = item.dataset.id;
console.log(id);
});
Tom Select позволяет создавать собственные события.
select.on('profile:updated', (data) => {
console.log(data);
});
Вызов:
select.trigger('profile:updated', {
id: 15,
status: 'saved'
});
Один обработчик может использоваться для нескольких сценариев.
function syncState() {
console.log('Синхронизация');
}
select.on('item_add', syncState);
select.on('item_remove', syncState);
select.on('clear', syncState);
В крупных приложениях события удобно собирать в отдельном модуле.
export function bindSelectEvents(select) {
select.on('change', onChange);
select.on('item_add', onAdd);
select.on('item_remove', onRemove);
select.on('dropdown_open', onOpen);
}
Tom Select корректно работает с async/await.
select.on('change', async (value) => {
try {
const response = await fetch('/api/save', {
method: 'POST',
body: JSON.stringify({ value })
});
const result = await response.json();
console.log(result);
} catch (error) {
console.error(error);
}
});
Ошибки в событиях необходимо перехватывать вручную.
select.on('item_add', (value) => {
try {
processItem(value);
} catch (error) {
console.error(error);
}
});
При интенсивном вводе событий может быть очень много.
Особенно это касается:
type;change;load.function debounce(callback, delay) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
callback(...args);
}, delay);
};
}
select.on('type', debounce((query) => {
console.log(query);
}, 300));
select.on('change', (value) => {
app.selectedCountry = value;
});
select.on('change', (value) => {
setState(value);
});
select.on('change', (value) => {
Alpine.store('form').country = value;
});
Типичная цепочка взаимодействия:
focusdropdown_opentypeloaditem_addchangedropdown_closeblurПонимание последовательности особенно важно при:
const select = new TomSelect('#users', {
valueField: 'id',
labelField: 'name',
searchField: 'name'
});
let loading = false;
select.on('type', async (query) => {
if (loading || query.length < 2) {
return;
}
loading = true;
try {
const response = await fetch(`/api/users?q=${query}`);
const users = await response.json();
select.clearOptions();
select.addOptions(users);
} finally {
loading = false;
}
});
select.on('item_add', (value) => {
console.log('Пользователь выбран:', value);
});
select.on('clear', () => {
console.log('Поиск очищен');
});