Множественная инициализация

В реальных интерфейсах редко используется единственный выпадающий список. Формы содержат группы селектов, фильтры повторяются в таблицах, элементы создаются динамически после загрузки данных. В таких условиях критично корректное управление множественной инициализацией компонентов, особенно при использовании 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 в декларативный слой управления поведением компонентов.


Повторная инициализация после изменения DOM

В SPA-подобных интерфейсах элементы могут удаляться и добавляться без перезагрузки страницы. В таких случаях требуется повторная инициализация только новых узлов.

function initTomSelect(context = document) {
  context.querySelectorAll('select').forEach((el) => {
    if (el.tomselect) return;
    new TomSelect(el);
  });
}

Функция может вызываться после:

  • подгрузки AJAX-данных
  • рендера шаблонов
  • вставки компонентов UI

Уничтожение экземпляров перед повторной инициализацией

В ситуациях, когда требуется полное обновление состояния, используется метод 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 и состояния библиотеки.


Использование MutationObserver для автоматической инициализации

Для полностью динамических интерфейсов применяется наблюдение за 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));
});

Такой подход снижает дублирование и упрощает масштабирование.


Особенности производительности при массовой инициализации

При инициализации большого количества экземпляров учитываются:

  • стоимость создания DOM-обёрток
  • количество обработчиков событий
  • нагрузка на поиск и фильтрацию

Оптимизация достигается за счёт:

  • пакетной инициализации после загрузки страницы
  • минимизации конфигураций
  • избежания повторных 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.tomselect
  • разделение конфигураций по классам и атрибутам
  • централизованные фабрики настроек
  • контроль жизненного цикла через destroy
  • адаптация к динамическому DOM через повторную инициализацию или MutationObserver
  • регистрация экземпляров для массового управления

Такая архитектура обеспечивает стабильную работу библиотеки в интерфейсах любой сложности, от статических форм до динамических приложений с постоянной перестройкой DOM.