Модификация DOM

Библиотека Tom Select строит интерфейс поверх исходного <select> или текстового input, создавая управляемую DOM-структуру, которая затем синхронизируется с внутренним состоянием. Основная особенность заключается в том, что исходный элемент не удаляется, а оборачивается в дополнительные контейнеры, через которые проходит вся визуальная и интерактивная логика.

После инициализации формируется базовый каркас:

  • внешний контейнер .tom-select
  • внутренний контрол .ts-control
  • поле ввода .ts-input
  • контейнер выбранных элементов .ts-item
  • выпадающий список .ts-dropdown

Каждый из этих узлов может быть модифицирован как через API, так и через переопределение шаблонов рендеринга.


Базовая обёртка и управление корневым элементом

Инициализация экземпляра приводит к оборачиванию исходного элемента:

new TomSelect("#select", {
  create: true
});

После выполнения создаётся структура вида:

<div class="tom-select">
  <div class="ts-control">
    <input class="ts-input">
  </div>
</div>

Корневой DOM-узел доступен через:

const instance = new TomSelect("#select");
instance.wrapper;

wrapper используется как точка входа для любых DOM-операций, связанных с компонентом. Добавление классов, атрибутов или интеграция с внешними UI-библиотеками выполняется именно через этот элемент.

instance.wrapper.classList.add("custom-theme");

Модификация контроллера ввода

Контрол (.ts-control) является контейнером, внутри которого располагаются выбранные элементы и поле ввода. Его структура динамически изменяется в зависимости от режима работы: single, multiple, create.

Доступ к DOM-контролу:

instance.control;

Добавление кастомных элементов внутрь контроллера возможно напрямую:

const badge = document.createElement("span");
badge.className = "ts-badge";
badge.textContent = "Активный фильтр";

instance.control.appendChild(badge);

Важно учитывать, что Tom Select пересоздаёт часть узлов при обновлении состояния, поэтому любые нестандартные элементы должны быть устойчивыми к перерендерам.


Управление выбранными элементами

Каждый выбранный элемент представляется узлом .ts-item. В режиме multiple таких узлов может быть множество.

Создание пользовательской модификации DOM выбранных элементов выполняется через перехват событий или через кастомный рендер.

Пример доступа к текущим элементам:

instance.control.querySelectorAll(".item")

Однако более стабильным способом считается использование API состояния:

instance.items.forEach(value => {
  const node = instance.getItem(value);
  node.classList.add("highlighted");
});

Манипуляция DOM-узлами выбранных элементов позволяет реализовать:

  • визуальные индикаторы состояния
  • вложенные элементы (иконки, бейджи)
  • динамическое изменение структуры отображения

Переопределение рендеринга элементов

Наиболее устойчивый способ модификации DOM в Tom Select — использование системы render. Она полностью контролирует создание узлов для options, items и dropdown.

new TomSelect("#select", {
  render: {
    option: function(data, escape) {
      return `
        <div class="option-custom">
          <span class="label">${escape(data.text)}</span>
        </div>
      `;
    },

    item: function(data, escape) {
      return `
        <div class="item-custom">
          ${escape(data.text)}
        </div>
      `;
    }
  }
});

Через render.option формируется структура выпадающего списка, а через render.item — отображение выбранных элементов.

Такой подход исключает необходимость прямого вмешательства в DOM после рендеринга и предотвращает рассинхронизацию состояния.


Модификация выпадающего списка

Выпадающий список .ts-dropdown создаётся динамически при фокусе и удаляется при потере активности. Доступ к нему осуществляется через:

instance.dropdown;

Добавление кастомных блоков:

instance.on("dropdown_open", () => {
  const header = document.createElement("div");
  header.className = "dropdown-header";
  header.textContent = "Результаты поиска";

  instance.dropdown.prepend(header);
});

Сценарии модификации dropdown включают:

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

Работа с опциями и optgroups в DOM

Каждая опция <option> из исходного select трансформируется во внутреннюю модель и затем в DOM-элемент внутри dropdown.

Получение соответствующего DOM-узла:

const node = instance.getOption(value);

Модификация:

node.classList.add("option-modified");
node.dataset.state = "active";

Optgroups также имеют собственную структуру:

const group = instance.getOptgroup(value);
group.classList.add("group-highlight");

Такая возможность позволяет:

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

Инъекция пользовательских элементов в dropdown

Dropdown не ограничен списком опций. Внутрь можно добавлять произвольные узлы, расширяя функциональность интерфейса.

instance.on("dropdown_open", () => {
  const footer = document.createElement("div");
  footer.className = "ts-footer";
  footer.textContent = "Дополнительные действия";

  instance.dropdown.appendChild(footer);
});

Такой подход часто используется для:

  • кнопок “создать новый элемент”
  • информационных панелей
  • отображения количества результатов

Синхронизация DOM и внутреннего состояния

Любая модификация DOM должна учитывать, что Tom Select поддерживает собственную модель данных. Прямое удаление или изменение узлов без обновления состояния приводит к рассинхронизации.

Корректные способы воздействия:

instance.addItem("value");
instance.removeItem("value");
instance.clear();

После этих операций DOM пересобирается автоматически.

Прямое удаление узла:

instance.getItem("value").remove();

возможно, но требует последующего обновления состояния:

instance.removeItem("value", true);

Кастомизация классов и атрибутов DOM

Добавление кастомных классов на уровне всей структуры:

instance.wrapper.classList.add("dark-theme");

Добавление классов на уровне control:

instance.control.classList.add("control-large");

Добавление атрибутов для интеграции с внешними системами:

instance.wrapper.setAttribute("data-component", "select");

Такие модификации позволяют:

  • интегрировать компонент в дизайн-системы
  • управлять стилями через CSS-переменные
  • подключать внешние UI-фреймворки

Обработка динамических изменений DOM

Tom Select активно обновляет DOM при изменении состояния ввода, выбора и фильтрации. Это означает, что любые ручные изменения должны учитывать повторный рендер.

Для безопасной модификации используются события:

instance.on("item_add", () => {
  instance.control.classList.add("has-items");
});

instance.on("item_remove", () => {
  if (instance.items.length === 0) {
    instance.control.classList.remove("has-items");
  }
});

Также полезны события:

  • dropdown_open
  • dropdown_close
  • change
  • load

Использование MutationObserver для расширенной модификации

При необходимости глубокой интеграции с DOM возможно использование MutationObserver, отслеживающего изменения внутри компонента.

const observer = new MutationObserver((mutations) => {
  mutations.forEach(mutation => {
    if (mutation.type === "childList") {
      // обработка изменений структуры
    }
  });
});

observer.observe(instance.wrapper, {
  childList: true,
  subtree: true
});

Такой подход применяется для:

  • синхронизации с внешними UI библиотеками
  • аналитики изменений интерфейса
  • адаптивной переработки DOM

Изменение структуры input внутри control

Поле ввода .ts-input является ключевым элементом для поиска и создания новых значений.

Доступ:

const input = instance.control.querySelector(".ts-input");

Модификация:

input.setAttribute("placeholder", "Поиск...");
input.classList.add("input-enhanced");

Также возможно добавление событий:

input.addEventListener("focus", () => {
  instance.wrapper.classList.add("focused");
});

Перестройка DOM через destroy и rebuild

При радикальных изменениях структуры используется пересоздание экземпляра:

instance.destroy();

new TomSelect("#select", {
  create: true
});

Это гарантирует:

  • очистку всех DOM-узлов
  • сброс обработчиков событий
  • повторную инициализацию структуры

Подход применяется при смене конфигурации на лету или переключении режимов интерфейса.