Механизм отображения выбранных значений в Tom Select основан на системе рендеринга, которая разделяет визуализацию выпадающего списка и выбранных элементов внутри control-зоны. Именно слой выбранных значений чаще всего требует кастомизации, поскольку именно он формирует интерфейс «чипов», тегов, аватаров, бейджей и сложных структур данных.
Внутренняя модель делит отображение на несколько независимых шаблонов:
render.item — элемент внутри dropdown спискаrender.option — визуализация опции в спискеrender.item (или render.selection в
некоторых конфигурациях) — отображение выбранного значенияrender.optgroup_header — заголовки группДля кастомизации выбранных значений ключевым является именно
render.item, поскольку он отвечает за то, как элемент
превращается в «чип» внутри control-блока.
Стандартная конфигурация может быть переопределена через объект
render:
new TomSelect("#select", {
render: {
item: function(data, escape) {
return `<div class="ts-item">${escape(data.text)}</div>`;
}
}
});
Здесь:
data — объект выбранной опцииescape — функция для безопасного экранирования
HTMLЧасто выбранные значения отображаются как структурированные элементы: аватары, метки статуса, дополнительные поля.
Пример расширенного шаблона:
new TomSelect("#users", {
render: {
item: function(data, escape) {
return `
<div class="user-chip">
<img class="avatar" src="${escape(data.avatar)}" />
<span class="name">${escape(data.name)}</span>
<span class="role">${escape(data.role)}</span>
</div>
`;
}
}
});
Такой подход превращает стандартный input в визуальный компонент уровня UI-фреймворка.
Важный аспект архитектуры заключается в том, что
render.option и render.item не обязаны
совпадать.
new TomSelect("#products", {
render: {
option: function(data, escape) {
return `<div class="option">${escape(data.title)} — ${escape(data.price)}</div>`;
},
item: function(data, escape) {
return `<div class="selected-product">${escape(data.title)}</div>`;
}
}
});
Такое разделение позволяет:
Отображение выбранных элементов часто требует изменения поведения контейнера control.
new TomSelect("#tags", {
maxItems: 3
});
При превышении лимита библиотека автоматически перестраивает control-зону.
Для глубокой интеграции с CSS-системами используется добавление классов внутри шаблонов:
new TomSelect("#select", {
render: {
item: function(data, escape) {
const typeClass = data.type ? `type-${data.type}` : "";
return `
<div class="ts-chip ${typeClass}">
${escape(data.text)}
</div>
`;
}
}
});
Это позволяет:
Каждый выбранный элемент хранит полный объект данных, включая кастомные поля.
Пример структуры:
{
value: "42",
text: "John Doe",
role: "admin",
avatar: "/img/john.png"
}
Отображение:
new TomSelect("#users", {
render: {
item: function(data, escape) {
return `
<div class="chip">
<img src="${escape(data.avatar)}" />
<span>${escape(data.text)}</span>
</div>
`;
}
}
});
Использование HTML в render.item требует строгого
экранирования всех динамических данных.
Функция escape обязательна для:
data.textИгнорирование экранирования приводит к XSS-уязвимостям, особенно при загрузке данных с API.
Отображение выбранных значений тесно связано с кнопкой удаления. Она добавляется автоматически, но может быть стилизована или переопределена через CSS:
.ts-chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
border-radius: 12px;
}
При необходимости кнопка удаления может быть скрыта:
new TomSelect("#select", {
plugins: {
remove_button: null
}
});
Изменение выбранных значений программно приводит к повторному вызову рендеринга:
const control = new TomSelect("#select");
control.addItem("value1");
control.addItem("value2");
control.updateItem("value1", {
text: "Updated label",
avatar: "/new.png"
});
После обновления UI автоматически перестраивает соответствующий чип.
Control-зона может быть дополнительно модифицирована через CSS-классы:
.ts-control — основной контейнер.ts-wrapper — общий wrapper.item — выбранный элементПример:
.ts-control {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
Это позволяет реализовать:
В режиме single selection выбранный элемент отображается внутри input-поля, а не как список чипов.
new TomSelect("#single", {
maxItems: 1,
render: {
item: function(data, escape) {
return `<div class="single-value">${escape(data.text)}</div>`;
}
}
});
Особенность:
Иногда требуется изменить структуру control-зоны полностью:
new TomSelect("#select", {
render: {
item: function(data, escape) {
return `
<span class="custom-tag">
<strong>${escape(data.text)}</strong>
</span>
`;
}
}
});
При этом внешний контейнер остаётся управляемым библиотекой, но содержимое полностью определяется разработчиком.
Отображение выбранных значений часто дополняется иконками:
new TomSelect("#icons", {
render: {
item: function(data, escape) {
return `
<div class="icon-item">
<i class="${escape(data.icon)}"></i>
<span>${escape(data.text)}</span>
</div>
`;
}
}
});
Такой подход используется в:
Для связи UI и логики можно добавлять data-атрибуты:
render: {
item: function(data, escape) {
return `
<div class="chip" data-id="${escape(data.value)}">
${escape(data.text)}
</div>
`;
}
}
Это позволяет:
Когда выбранных элементов становится много, применяется стратегия overflow:
Пример кастомного ограничения:
render: {
item: function(data, escape) {
if (data.hidden) {
return `<div class="chip chip--hidden"></div>`;
}
return `<div class="chip">${escape(data.text)}</div>`;
}
}
Отображение часто синхронизируется с событиями:
const select = new TomSelect("#select", {
render: {
item: function(data, escape) {
return `<div class="chip">${escape(data.text)}</div>`;
}
}
});
select.on("item_add", function(value, item) {
item.classList.add("added-animation");
});
Это позволяет добавлять анимации появления выбранных элементов.
Сложные интерфейсы часто комбинируют несколько источников данных:
render: {
item: function(data, escape) {
const badge = data.isNew ? `<span class="badge">NEW</span>` : "";
return `
<div class="chip">
${escape(data.text)}
${badge}
</div>
`;
}
}
Такие конструкции превращают control-зону в полноценный UI-компонент с логикой отображения состояния.