Библиотека 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 включают:
Каждая опция <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");
Такая возможность позволяет:
Dropdown не ограничен списком опций. Внутрь можно добавлять произвольные узлы, расширяя функциональность интерфейса.
instance.on("dropdown_open", () => {
const footer = document.createElement("div");
footer.className = "ts-footer";
footer.textContent = "Дополнительные действия";
instance.dropdown.appendChild(footer);
});
Такой подход часто используется для:
Любая модификация DOM должна учитывать, что Tom Select поддерживает собственную модель данных. Прямое удаление или изменение узлов без обновления состояния приводит к рассинхронизации.
Корректные способы воздействия:
instance.addItem("value");
instance.removeItem("value");
instance.clear();
После этих операций DOM пересобирается автоматически.
Прямое удаление узла:
instance.getItem("value").remove();
возможно, но требует последующего обновления состояния:
instance.removeItem("value", true);
Добавление кастомных классов на уровне всей структуры:
instance.wrapper.classList.add("dark-theme");
Добавление классов на уровне control:
instance.control.classList.add("control-large");
Добавление атрибутов для интеграции с внешними системами:
instance.wrapper.setAttribute("data-component", "select");
Такие модификации позволяют:
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_opendropdown_closechangeloadПри необходимости глубокой интеграции с DOM возможно использование
MutationObserver, отслеживающего изменения внутри
компонента.
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === "childList") {
// обработка изменений структуры
}
});
});
observer.observe(instance.wrapper, {
childList: true,
subtree: true
});
Такой подход применяется для:
Поле ввода .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");
});
При радикальных изменениях структуры используется пересоздание экземпляра:
instance.destroy();
new TomSelect("#select", {
create: true
});
Это гарантирует:
Подход применяется при смене конфигурации на лету или переключении режимов интерфейса.