Опции отображения

Система отображения в библиотеке строится вокруг механизма шаблонов рендеринга, который определяет, как именно выглядят элементы списка, выбранные значения, группы и вспомогательные состояния. В отличие от нативного <select>, визуальная часть полностью контролируется JavaScript-слоем, что позволяет гибко управлять структурой DOM и содержимым элементов.

Основой служит конфигурация render, а также набор полей данных, таких как labelField, valueField, optgroupField, disabledField. Эти параметры определяют, какие свойства объекта используются при построении интерфейса.


Базовая модель отображения данных

Каждый элемент данных в списке представляет собой объект:

{
  value: "1",
  text: "Option 1"
}

Однако в реальных сценариях структура часто расширяется:

{
  id: 1,
  title: "Option 1",
  description: "Дополнительное описание",
  category: "group-a"
}

Для корректного отображения требуется настройка:

  • valueField: поле уникального значения
  • labelField: поле отображаемого текста
  • optgroupField: поле группировки

Пример:

new TomSelect("#select", {
  valueField: "id",
  labelField: "title",
  optgroupField: "category",
  searchField: ["title", "description"],
  options: [
    { id: 1, title: "Alpha", category: "A" },
    { id: 2, title: "Beta", category: "B" }
  ]
});

Система рендеринга render

Ключевой механизм визуализации — объект render, содержащий функции-шаблоны. Каждая функция возвращает HTML-строку или DOM-структуру.

Основные шаблоны:

  • option — элемент выпадающего списка
  • item — выбранный элемент
  • option_create — создание нового элемента
  • optgroup_header — заголовок группы
  • optgroup — контейнер группы

Отображение опций (render.option)

Функция отвечает за внешний вид каждой строки в выпадающем списке.

new TomSelect("#select", {
  render: {
    option: function(data, escape) {
      return `
        <div>
          <span class="title">${escape(data.title)}</span>
          <span class="desc">${escape(data.description)}</span>
        </div>
      `;
    }
  }
});

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

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

Функция вызывается для каждой опции при открытии dropdown.


Отображение выбранных элементов (render.item)

После выбора элемент переносится в область активных значений.

render: {
  item: function(data, escape) {
    return `<div class="item-selected">
              ${escape(data.title)}
            </div>`;
  }
}

Отличия от option:

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

Создание новых элементов (render.option_create)

Если включён режим создания новых значений (create: true), появляется специальный шаблон.

render: {
  option_create: function(data, escape) {
    return `<div class="create">Добавить: ${escape(data.input)}</div>`;
  }
}

Поведение:

  • data.input содержит введённый текст
  • элемент отображается внизу списка
  • при выборе создаётся новый объект

Группировка (optgroup и optgroup_header)

Группы позволяют структурировать список.

new TomSelect("#select", {
  optgroups: [
    { value: "A", label: "Группа A" },
    { value: "B", label: "Группа B" }
  ],
  optgroupField: "group"
});

Заголовок группы:

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

Контейнер группы:

render: {
  optgroup: function(data, escape) {
    return `<div class="group">
              ${data.options_html}
            </div>`;
  }
}

Экранирование и безопасность вывода

Функция escape является обязательным инструментом при построении шаблонов.

Она предотвращает внедрение HTML-кода через данные:

escape("<script>alert(1)</script>")

Результат преобразуется в безопасную строку.

Игнорирование escape допустимо только при полностью доверенных источниках данных, что в UI-слое считается исключением.


Условная логика внутри render-функций

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

render: {
  option: function(data, escape) {
    let status = data.active ? "active" : "inactive";

    return `
      <div class="option ${status}">
        ${escape(data.title)}
      </div>
    `;
  }
}

Применяются:

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

Изменение структуры DOM через render

Каждый шаблон напрямую влияет на DOM-структуру:

  • создаются контейнеры .option
  • формируются .item
  • добавляются группы .optgroup

Это означает, что стилизация полностью зависит от разработанной разметки.

Пример кастомного layout:

render: {
  option: function(data, escape) {
    return `
      <div class="row">
        <div class="col-title">${escape(data.title)}</div>
        <div class="col-meta">${escape(data.meta)}</div>
      </div>
    `;
  }
}

Управление визуальной плотностью

Отображение может варьироваться от компактного до расширенного:

Компактный вариант

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

Расширенный вариант

render: {
  option: (data, escape) => `
    <div class="card">
      <strong>${escape(data.title)}</strong>
      <p>${escape(data.description)}</p>
      <small>${escape(data.category)}</small>
    </div>
  `
}

Влияние CSS на отображение render-структур

Хотя render отвечает за HTML, финальный вид определяется стилями:

  • позиционирование dropdown
  • прокрутка списка
  • выделение активного элемента
  • hover-состояния
  • состояния disabled

Базовая структура классов:

  • .ts-control
  • .ts-dropdown
  • .option
  • .item
  • .active
  • .selected

Работа с кастомными полями данных

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

new TomSelect("#select", {
  labelField: "name",
  valueField: "uuid",
  searchField: ["name", "tags"]
});

Если поля не заданы корректно, визуальный слой может:

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

Динамическое обновление render-шаблонов

Шаблоны могут изменяться после инициализации:

const control = new TomSelect("#select");

control.settings.render.option = function(data, escape) {
  return `<div>${escape(data.title)}</div>`;
};

control.refreshOptions(false);

Поведение:

  • перерисовка списка
  • сохранение выбранных значений
  • обновление DOM без пересоздания экземпляра

Ограничения системы отображения

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

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

Итоговая структура визуального слоя

Отображение строится по следующей цепочке:

  1. Данные (options)
  2. Поля конфигурации (labelField, valueField)
  3. Группировка (optgroupField)
  4. Шаблоны render
  5. Генерация DOM
  6. Применение CSS-стилей
  7. Обновление состояния интерфейса