В библиотеке Tom Select отображение элементов строится на системе рендер-функций (render functions), которые позволяют полностью контролировать HTML-разметку как выбранных значений, так и выпадающего списка. Центральная идея заключается в том, что каждая часть интерфейса не фиксирована, а формируется через набор шаблонов, переопределяемых разработчиком.
Основной объект кастомизации — параметр render,
принимающий набор функций:
option — отображение элемента в спискеitem — отображение выбранного элементаoption_create — отображение создаваемого элементаoptgroup_header — заголовки группno_results — состояние отсутствия результатовloading — индикатор загрузкиКаждая функция получает два аргумента: data (данные
элемента) и escape (функция экранирования HTML).
new TomSelect("#select", {
render: {
option: function(data, escape) {
return `<div>${escape(data.text)}</div>`;
},
item: function(data, escape) {
return `<span>${escape(data.text)}</span>`;
}
}
});
Такой подход позволяет отделить данные от представления и полностью контролировать DOM-структуру.
Каждый элемент, передаваемый в Tom Select, представляет собой объект,
содержащий минимум поле text. Однако реальная гибкость
достигается за счёт расширения структуры:
value — значение элементаtext — отображаемый текстimage, description,
badge, meta)Эти дополнительные поля напрямую используются внутри render-функций.
{
value: "js",
text: "JavaScript",
image: "/icons/js.png",
description: "Язык программирования",
badge: "pop"
}
Использование расширенных данных позволяет создавать сложные UI-компоненты прямо внутри выпадающего списка.
Функция option отвечает за отображение элементов внутри
dropdown. Это ключевая точка кастомизации, так как именно здесь
формируется визуальная структура списка.
Пример добавления изображения и описания:
render: {
option: function(data, escape) {
return `
<div class="option">
<img src="${escape(data.image)}" class="option-avatar">
<div class="option-body">
<div class="option-title">${escape(data.text)}</div>
<div class="option-desc">${escape(data.description)}</div>
</div>
</div>
`;
}
}
При такой реализации структура становится многоуровневой, а не ограничивается строкой текста. Это особенно важно для справочников, пользователей, товаров и тегов.
Отображение выбранного элемента часто требует более компактной и
визуально упрощённой формы. Функция item отвечает за то,
как выбранные значения отображаются внутри инпута.
render: {
item: function(data, escape) {
return `
<div class="selected-item">
<span class="label">${escape(data.text)}</span>
<span class="badge">${escape(data.badge)}</span>
</div>
`;
}
}
Особенность заключается в том, что элементы item живут в
ограниченном пространстве input-области, поэтому структура должна
учитывать переполнение, переносы и ограниченную ширину контейнера.
Несмотря на схожесть, option и item имеют
принципиальные различия:
option отображается в списке выбораitem отображается после выбораoption может быть более детализированнымitem должен быть компактным и устойчивым к ограничению
шириныВ сложных интерфейсах часто применяется принцип «богатый option — упрощённый item».
При использовании группировки данных Tom Select предоставляет
optgroup_header. Он отвечает за визуальное оформление
заголовков групп.
render: {
optgroup_header: function(data, escape) {
return `
<div class="optgroup-header">
${escape(data.label)}
</div>
`;
}
}
Группировка часто применяется в больших справочниках, где важно структурировать элементы по категориям, регионам или типам.
Кастомизация состояний интерфейса позволяет улучшить UX при динамическом поиске.
no_results используется, когда поиск не дал
совпадений:
render: {
no_results: function(data, escape) {
return `<div class="no-results">Ничего не найдено</div>`;
}
}
loading отображается при асинхронной загрузке
данных:
render: {
loading: function(data, escape) {
return `<div class="loading-state">Загрузка...</div>`;
}
}
Эти состояния особенно важны при использовании remote data и AJAX-запросов.
Функция escape является встроенным механизмом защиты от
внедрения вредоносного HTML. Она обязательна при вставке любых данных,
полученных извне.
Неправильный подход:
return `<div>${data.text}</div>`;
Правильный подход:
return `<div>${escape(data.text)}</div>`;
При кастомизации сложных шаблонов рекомендуется экранировать все динамические значения, кроме заранее безопасных HTML-структур.
Render-функции допускают включение интерактивных элементов, включая кнопки и индикаторы. Однако следует учитывать, что Tom Select повторно рендерит элементы при обновлении состояния.
Пример добавления кнопки удаления:
render: {
item: function(data, escape) {
return `
<div class="tag-item">
<span>${escape(data.text)}</span>
<button type="button" class="remove">×</button>
</div>
`;
}
}
В таком случае обработка событий должна быть делегирована, так как DOM пересоздаётся.
Хотя Tom Select не навязывает строгую структуру HTML, рекомендуется придерживаться согласованной системы классов:
ts-,
select-, option-)header,
body, meta)Это упрощает поддержку CSS и предотвращает конфликты при интеграции в крупные проекты.
Кастомизация render-функций часто используется для интеграции с дизайн-системами. В таких случаях структура HTML должна соответствовать компонентной модели:
Пример:
render: {
option: function(data, escape) {
return `
<div class="ds-option">
<div class="ds-icon">${escape(data.icon)}</div>
<div class="ds-content">
<div class="ds-title">${escape(data.text)}</div>
<div class="ds-meta">${escape(data.meta)}</div>
</div>
</div>
`;
}
}
При сложной кастомизации важно учитывать влияние на производительность:
Оптимизация достигается за счёт:
Для крупных проектов целесообразно выносить render-функции в отдельные модули. Это позволяет поддерживать единый стиль отображения во всех инстансах Tom Select.
export const renderTemplates = {
option: (data, escape) => `...`,
item: (data, escape) => `...`
};
И затем использовать их повторно:
new TomSelect("#select", {
render: renderTemplates
});
Такой подход снижает дублирование кода и упрощает масштабирование интерфейса.