В библиотеке Tom Select механизм рендеринга отвечает за формирование
HTML-структуры элементов интерфейса: выбранных значений, выпадающих
пунктов, групп опций, состояния загрузки и служебных сообщений. В
отличие от стандартных <select>, где визуальное
представление ограничено возможностями браузера, здесь каждый фрагмент
интерфейса полностью контролируется через набор функций
render.
Объект render передаётся в конфигурации при
инициализации и содержит набор функций, каждая из которых возвращает
HTML-строку.
Ключевая идея заключается в разделении логики данных и представления:
{value, text, ...}Базовая структура:
new TomSelect("#select", {
render: {
option: function(data, escape) {
return `<div>${escape(data.text)}</div>`;
}
}
});
Функция escape критически важна — она предотвращает
XSS-уязвимости при вставке пользовательских данных.
option отвечает за отображение элемента в выпадающем
списке.
render: {
option: function(data, escape) {
return `
<div class="option">
<span class="title">${escape(data.text)}</span>
<span class="meta">${escape(data.value)}</span>
</div>
`;
}
}
Особенности:
item формирует отображение выбранного элемента внутри
input-зоны.
render: {
item: function(data, escape) {
return `
<div class="item">
${escape(data.text)}
</div>
`;
}
}
Разделение option и item позволяет
создавать разные представления одного и того же объекта в зависимости от
контекста.
option_create используется при включённой возможности
добавления новых элементов пользователем
(create: true).
render: {
option_create: function(data, escape) {
return `
<div class="create">
Добавить: <strong>${escape(data.input)}</strong>
</div>
`;
}
}
Особенности:
data.input содержит строку, введённую
пользователемГруппировка опций реализуется через optgroup_header.
render: {
optgroup_header: function(data, escape) {
return `
<div class="optgroup-header">
${escape(data.label)}
</div>
`;
}
}
Используется при работе с вложенными структурами данных:
{
optgroups: [
{ value: "fruits", label: "Фрукты" }
],
options: [
{ value: "apple", text: "Яблоко", optgroup: "fruits" }
]
}
no_results отображает состояние отсутствия совпадений
при поиске.
render: {
no_results: function(data, escape) {
return `
<div class="no-results">
Ничего не найдено
</div>
`;
}
}
Это состояние активируется после фильтрации списка.
loading используется при асинхронной подгрузке данных
(load функция).
render: {
loading: function(data, escape) {
return `
<div class="loading">
Загрузка...
</div>
`;
}
}
Особенности:
Каждая функция получает объект data, структура которого
зависит от типа элемента:
option / item:
{value, text, ...customFields}option_create: {input}optgroup_header: {label}loading / no_results: служебные
объектыДополнительные поля можно свободно добавлять при инициализации:
options: [
{ value: "1", text: "Apple", color: "red" }
]
И использовать их в рендере:
render: {
option: function(data, escape) {
return `
<div>
<span>${escape(data.text)}</span>
<small>${escape(data.color)}</small>
</div>
`;
}
}
Каждая render-функция получает второй аргумент
escape.
Его задача — экранирование HTML-символов:
< → <> → >& → &Пример использования:
escape(data.text)
Игнорирование escape приводит к потенциальной
возможности внедрения HTML/JS-кода в DOM, особенно при загрузке данных с
сервера или пользовательского ввода.
Tom Select позволяет заменить весь набор рендер-функций:
new TomSelect("#select", {
render: {
option: function(data, escape) {
return `<div>${escape(data.text)}</div>`;
},
item: function(data, escape) {
return `<span>${escape(data.text)}</span>`;
},
no_results: function() {
return `<div>Нет данных</div>`;
}
}
});
При отсутствии конкретного render-метода используется стандартная реализация библиотеки.
Render-функции допускают любую логику ветвления:
render: {
option: function(data, escape) {
if (data.disabled) {
return `<div class="disabled">${escape(data.text)}</div>`;
}
return `<div>${escape(data.text)}</div>`;
}
}
Таким образом можно:
Render-функции вызываются часто, особенно при:
Оптимизационные практики:
Tom Select не ограничивает структуру данных. Render-слой может использовать любые поля:
{
value: "js",
text: "JavaScript",
icon: "?",
description: "Язык программирования"
}
render: {
option: function(data, escape) {
return `
<div class="option">
<span class="icon">${data.icon}</span>
<div class="content">
<div class="title">${escape(data.text)}</div>
<div class="desc">${escape(data.description)}</div>
</div>
</div>
`;
}
}
Render-слой тесно связан с внутренними состояниями:
Каждое состояние может влиять на то, какие render-функции вызываются и какие данные передаются.
При сложных интерфейсах render-функции часто выносятся в отдельные шаблоны:
function renderOption(data, escape) {
return `
<div class="option">
${escape(data.text)}
</div>
`;
}
new TomSelect("#select", {
render: {
option: renderOption
}
});
Это упрощает поддержку и повторное использование логики представления.
Render-слой может использоваться для:
Пример карточного варианта:
render: {
option: function(data, escape) {
return `
<div class="card">
<img src="${data.avatar}" />
<div>
<strong>${escape(data.text)}</strong>
</div>
</div>
`;
}
}