Шаблонизация с HTML

Модель шаблонизации в библиотеке

В основе системы отображения элементов в Tom Select лежит набор функций render, отвечающих за генерацию HTML-структур отдельных частей интерфейса. Каждый визуальный компонент — опция, выбранный элемент, группа, заголовок, сообщение загрузки — формируется через шаблонную функцию, возвращающую HTML-строку.

Подход не использует полноценные шаблонизаторы или виртуальный DOM. Вместо этого применяется прямое формирование строк HTML с последующей вставкой в DOM через внутренние механизмы библиотеки. Это обеспечивает высокую производительность и предсказуемость рендеринга.

Ключевой принцип: каждый элемент UI является независимой единицей, формируемой отдельным шаблоном.


Базовая структура render-объекта

Внутренняя конфигурация рендеринга задаётся через объект render, передаваемый при инициализации:

new TomSelect('#select', {
  render: {
    option: function(data, escape) {
      return `<div>${escape(data.text)}</div>`;
    }
  }
});

Каждая функция получает:

  • data — объект данных элемента (option, item, group)
  • escape — функция экранирования HTML
  • self (в некоторых контекстах) — ссылка на экземпляр селекта

Шаблон option: базовая единица списка

Шаблон option определяет внешний вид элементов выпадающего списка.

render: {
  option: (data, escape) => {
    return `
      <div class="option">
        <span class="label">${escape(data.text)}</span>
      </div>
    `;
  }
}

Особенности:

  • используется только в dropdown-списке
  • не влияет на отображение выбранного значения
  • поддерживает любые пользовательские поля объекта data
  • может включать изображения, иконки, бейджи

Пример расширенного шаблона:

option: (data, escape) => `
  <div class="option">
    <img src="${escape(data.avatar)}" class="avatar">
    <div class="content">
      <div class="title">${escape(data.text)}</div>
      <div class="subtitle">${escape(data.role)}</div>
    </div>
  </div>
`

Шаблон item: выбранные элементы

item отвечает за отображение выбранных значений внутри инпута.

render: {
  item: (data, escape) => `
    <div class="item">
      ${escape(data.text)}
    </div>
  `
}

Отличие от option:

  • отображается в поле ввода
  • может включать кнопку удаления
  • поддерживает multi-select контекст
  • должен быть компактным по структуре

Расширенный пример:

item: (data, escape) => `
  <div class="item">
    <span class="tag">${escape(data.text)}</span>
    <span class="remove">&times;</span>
  </div>
`

Шаблоны группировки (optgroup)

Для группированных списков используется optgroup_header.

render: {
  optgroup_header: (data, escape) => `
    <div class="optgroup-header">
      ${escape(data.label)}
    </div>
  `
}

Роль групп:

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

Группы не являются интерактивными элементами выбора, но могут быть стилизованы как секции.


Шаблоны состояния загрузки

При асинхронной подгрузке данных используется loading.

render: {
  loading: () => `
    <div class="loading">
      Загрузка данных...
    </div>
  `
}

Особенности:

  • отображается при load() или async fetch
  • заменяет список опций временно
  • может быть анимированным индикатором

Шаблон no_results

Отображается при отсутствии совпадений в фильтре.

render: {
  no_results: () => `
    <div class="no-results">
      Ничего не найдено
    </div>
  `
}

Используется в механизме поиска по списку и может зависеть от searchField.


Позволяют добавлять статические блоки в верхнюю и нижнюю часть списка.

render: {
  dropdown_header: () => `
    <div class="dropdown-header">
      Выберите значение
    </div>
  `,
  dropdown_footer: () => `
    <div class="dropdown-footer">
      Всего элементов: 120
    </div>
  `
}

Применение:

  • информационные панели
  • фильтры
  • дополнительные действия (например, кнопки)

Работа с функцией escape

Функция escape критична для безопасности. Она предотвращает XSS-уязвимости при вставке пользовательских данных в HTML.

Пример:

render: {
  option: (data, escape) => `
    <div>${escape(data.text)}</div>
  `
}

Что экранируется:

  • <
  • >
  • &
  • "
  • '

Игнорирование escape допустимо только при полной уверенности в безопасности источника данных.


Пользовательские HTML-поля данных

Шаблоны напрямую работают с произвольными полями объектов:

{
  value: 1,
  text: "Разработка",
  icon: "/icons/dev.svg",
  meta: {
    count: 42
  }
}

Использование:

option: (data, escape) => `
  <div>
    <img src="${escape(data.icon)}">
    <span>${escape(data.text)}</span>
    <small>${escape(data.meta.count)}</small>
  </div>
`

Различие между option и item в архитектуре

Тип Контекст Назначение
option dropdown выбор элемента
item input отображение выбранного

Разделение позволяет независимо контролировать визуальные представления одного и того же набора данных.


Кастомизация через CSS-классы внутри шаблонов

HTML-шаблоны тесно связаны со стилями. Обычно используются предсказуемые классы:

  • option
  • item
  • optgroup-header
  • no-results

Пример:

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

Такой подход позволяет комбинировать стандартные стили Tom Select и пользовательские темы.


Динамическая логика внутри шаблонов

Шаблоны могут содержать условные конструкции:

option: (data, escape) => `
  <div class="option">
    ${data.disabled
      ? `<span class="disabled">${escape(data.text)}</span>`
      : `<span>${escape(data.text)}</span>`
    }
  </div>
`

Применение:

  • скрытие состояния
  • отображение ролей пользователей
  • индикация доступности

Встраивание SVG и иконок

Шаблоны поддерживают inline SVG:

item: (data, escape) => `
  <div class="item">
    <svg class="icon" viewBox="0 0 24 24">
      <path d="..."></path>
    </svg>
    ${escape(data.text)}
  </div>
`

SVG предпочтительнее raster-иконок при высокой плотности интерфейсов.


Производительность шаблонов

Рендеринг в Tom Select оптимизирован под частые обновления списка. Однако сложные шаблоны могут влиять на производительность.

Факторы влияния:

  • количество DOM-узлов в шаблоне
  • частота пересоздания списка
  • использование тяжелых вычислений внутри render-функций

Рекомендуется:

  • избегать сложной логики внутри render
  • кешировать вычисляемые поля
  • минимизировать вложенность HTML

Переиспользование шаблонов

Для унификации интерфейса шаблоны выносятся в отдельные функции:

const renderOption = (data, escape) => `
  <div class="option">${escape(data.text)}</div>
`;

new TomSelect('#select', {
  render: {
    option: renderOption,
    item: renderOption
  }
});

Это снижает дублирование и упрощает поддержку UI.


Ограничения HTML-шаблонов

Несмотря на гибкость, система имеет ограничения:

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

Тем не менее, такая модель обеспечивает высокую скорость работы и предсказуемое поведение интерфейса без дополнительных абстракций.