В реальных интерфейсах редко используется единственный выпадающий список. Формы содержат группы селектов, фильтры повторяются в таблицах, элементы создаются динамически после загрузки данных. В таких условиях критично корректное управление множественной инициализацией компонентов, особенно при использовании Tom Sel ect.
Библиотека Tom Select предоставляет гибкий механизм создания
экземпляров поверх <select> элементов, однако при
работе с несколькими экземплярами одновременно появляются вопросы:
повторная инициализация, единая конфигурация, управление жизненным
циклом, предотвращение дублирования экземпляров и корректное обновление
DOM.
Каждый select элемент в DOM может быть преобразован в
независимый экземпляр Tom Select:
document.querySelectorAll('select').forEach((el) => {
new TomSelect(el);
});
В этой модели каждый элемент получает собственное состояние, список опций, обработчики событий и внутренний store. Важно учитывать, что повторный вызов конструктора на уже инициализированном элементе приводит к наложению экземпляров и непредсказуемому поведению.
При динамических интерфейсах часто возникает повторный проход по DOM. Для предотвращения дублирования используется проверка наличия экземпляра:
document.querySelectorAll('select').forEach((el) => {
if (!el.tomselect) {
new TomSelect(el);
}
});
Свойство tomselect хранит ссылку на уже созданный
экземпляр. Это основной механизм контроля жизненного цикла в рамках
стандартного использования библиотеки.
В проектах с большим числом селектов целесообразно использовать фабричный подход, при котором конфигурация централизована:
const baseConfig = {
create: false,
allowEmptyOption: true,
maxItems: 1
};
document.querySelectorAll('.ts-select').forEach((el) => {
new TomSelect(el, baseConfig);
});
Такая модель позволяет:
При необходимости конфигурация расширяется на уровне конкретного
элемента через data-* атрибуты.
Комбинирование общей и локальной конфигурации позволяет сохранять баланс между унификацией и гибкостью:
document.querySelectorAll('.ts-select').forEach((el) => {
const config = {
maxItems: el.dataset.maxItems ? Number(el.dataset.maxItems) : 1,
placeholder: el.dataset.placeholder || 'Select...',
create: el.dataset.create === 'true'
};
new TomSelect(el, config);
});
Использование dataset превращает HTML в декларативный
слой управления поведением компонентов.
В SPA-подобных интерфейсах элементы могут удаляться и добавляться без перезагрузки страницы. В таких случаях требуется повторная инициализация только новых узлов.
function initTomSelect(context = document) {
context.querySelectorAll('select').forEach((el) => {
if (el.tomselect) return;
new TomSelect(el);
});
}
Функция может вызываться после:
В ситуациях, когда требуется полное обновление состояния,
используется метод destroy:
document.querySelectorAll('select').forEach((el) => {
if (el.tomselect) {
el.tomselect.destroy();
}
new TomSelect(el);
});
Этот подход применяется при:
Важно учитывать, что destroy() возвращает элемент к
исходному состоянию, удаляя созданные DOM-обёртки и обработчики
событий.
В сложных интерфейсах элементы часто разделяются по логическим группам:
new TomSelect('#country');
new TomSelect('.tags-select');
new TomSelect('[data-role="autocomplete"]');
Такой подход позволяет создавать независимые группы конфигураций без ручного перебора DOM.
При множественной инициализации часто используется паттерн «классовой маршрутизации»:
document.querySelectorAll('.ts-single').forEach((el) => {
new TomSelect(el, { maxItems: 1 });
});
document.querySelectorAll('.ts-multiple').forEach((el) => {
new TomSelect(el, { maxItems: null });
});
Каждый CSS-класс определяет поведение компонента, что упрощает масштабирование интерфейса.
При добавлении новых элементов в DOM необходимо учитывать момент их появления:
const container = document.querySelector('#form');
const newSelect = document.createElement('select');
newSelect.className = 'ts-select';
container.appendChild(newSelect);
new TomSelect(newSelect);
Если элементы создаются пакетно, применяется единый проход инициализации после вставки.
В таблицах с фильтрами или списках с пагинацией элементы могут перерисовываться регулярно. В таких случаях критично избегать накопления экземпляров:
function reinitTableFilters() {
document.querySelectorAll('.filter-select').forEach((el) => {
if (el.tomselect) el.tomselect.destroy();
new TomSelect(el);
});
}
Такой подход обеспечивает синхронизацию DOM и состояния библиотеки.
Для полностью динамических интерфейсов применяется наблюдение за DOM:
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType !== 1) return;
const selects = node.matches('select')
? [node]
: node.querySelectorAll('select');
selects.forEach((el) => {
if (!el.tomselect) {
new TomSelect(el);
}
});
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
Этот механизм позволяет автоматически подключать Tom Select ко всем новым элементам без ручного вызова инициализации.
При множественных экземплярах важно учитывать единообразие состояния:
Пример синхронизации нескольких селектов:
const selects = document.querySelectorAll('.sync-select');
const instances = Array.fr om(selects).map((el) => new TomSelect(el));
instances.forEach((instance) => {
instance.on('change', (value) => {
instances.forEach((other) => {
if (other !== instance) {
other.setValue(value);
}
});
});
});
При большом количестве элементов конфигурации выносятся в функции:
function createSelectConfig(type) {
const configs = {
simple: { maxItems: 1, create: false },
tags: { maxItems: null, create: true },
search: { loadThrottle: 300 }
};
return configs[type] || {};
}
document.querySelectorAll('select[data-type]').forEach((el) => {
new TomSelect(el, createSelectConfig(el.dataset.type));
});
Такой подход снижает дублирование и упрощает масштабирование.
При инициализации большого количества экземпляров учитываются:
Оптимизация достигается за счёт:
querySelectorAll вызововconst selects = document.getElementById('form').querySelectorAll('select');
for (let i = 0; i < selects.length; i++) {
if (!selects[i].tomselect) {
new TomSelect(selects[i]);
}
}
В сложных интерфейсах требуется централизованное управление всеми экземплярами:
const registry = [];
function registerTomSelect(instance) {
registry.push(instance);
}
function destroyAll() {
registry.forEach((instance) => instance.destroy());
registry.length = 0;
}
Регистрация позволяет контролировать состояние всех компонентов и выполнять массовые операции.
Множественная инициализация Tom Select строится на нескольких принципах:
el.tomselectdestroyТакая архитектура обеспечивает стабильную работу библиотеки в интерфейсах любой сложности, от статических форм до динамических приложений с постоянной перестройкой DOM.