HTML в опциях

Работа с HTML внутри опций в Slim Select требует понимания того, как библиотека обрабатывает данные элемента <select> и каким образом формируется DOM-структура выпадающего списка. В отличие от стандартного HTML <select>, где содержимое <option> интерпретируется как текст, Slim Select предоставляет расширенный слой рендеринга, позволяющий использовать HTML-разметку в элементах списка и в выбранном значении.

Slim Select опирается не на DOM <option> напрямую, а на внутреннюю структуру данных:

{
  text: 'Отображаемый текст',
  value: 'unique-value'
}

При этом text по умолчанию интерпретируется как строка и экранируется при выводе. Это означает, что HTML внутри text не будет выполнен, а отобразится как обычный текст.

Для поддержки HTML Slim Select использует расширенное поле:

{
  text: 'Обычный текст',
  html: '<strong>HTML текст</strong>',
  value: '1'
}

Именно html становится ключевым механизмом включения разметки в опции.

Включение HTML в опциях

При использовании массива данных HTML может быть задан напрямую:

new SlimSelect({
  select: '#select',
  data: [
    {
      value: '1',
      text: 'Google',
      html: '<span class="option option--google">Google</span>'
    },
    {
      value: '2',
      text: 'Facebook',
      html: '<span class="option option--facebook">Facebook</span>'
    }
  ]
})

В этом случае Slim Select использует html для рендера выпадающего списка, а text — как fallback и как источник для поиска.

Поведение при поиске

Поиск по списку выполняется по text, а не по html. Это важное поведение, которое позволяет:

  • сохранять чистую семантику данных
  • не учитывать HTML-теги в фильтрации
  • ускорять сравнение строк

Рендеринг DOM-элементов

При открытии списка Slim Select формирует структуру примерно следующего вида:

<div class="ss-option">
  <span class="option option--google">Google</span>
</div>

Если html не задан, используется:

<div class="ss-option">Google</div>

Таким образом, HTML внедряется не в <option>, а в отдельный контейнер внутри компонента.

Использование HTML через кастомные шаблоны

Slim Select позволяет переопределять шаблоны рендера через конфигурации template (в зависимости от версии API). Основные точки расширения:

  • option
  • selected
  • group

Пример кастомного рендера опции:

new SlimSelect({
  select: '#select',
  data: [
    { value: '1', text: 'HTML' },
    { value: '2', text: 'CSS' }
  ],
  template: {
    option: (option) => {
      return `<div class="custom-option">
                <span class="label">${option.text}</span>
              </div>`
    },
    selected: (option) => {
      return `<div class="custom-selected">
                ${option.text}
              </div>`
    }
  }
})

В этом режиме библиотека перестает ограничиваться стандартным отображением и полностью делегирует HTML-структуру разработчику.

Вставка иконок и декоративных элементов

HTML в опциях чаще всего используется для добавления визуальных элементов:

{
  value: 'ru',
  text: 'Русский',
  html: `<div class="lang-option">
           <img src="/flags/ru.svg" class="flag">
           <span>Русский</span>
         </div>`
},
{
  value: 'en',
  text: 'English',
  html: `<div class="lang-option">
           <img src="/flags/gb.svg" class="flag">
           <span>English</span>
         </div>`
}

Такой подход позволяет создавать интерфейсы уровня UI-kit без необходимости писать отдельный компонент.

Ограничения HTML в опциях

Несмотря на гибкость, HTML в Slim Select имеет ряд ограничений:

1. Поиск не учитывает HTML

Поиск всегда работает только по text:

  • HTML-теги игнорируются
  • вложенные элементы не участвуют в фильтрации

2. Не все события делегируются внутрь HTML

Если внутри html используются интерактивные элементы (кнопки, ссылки), они могут не получать корректные события без дополнительной обработки через делегирование DOM.

3. Внутренний рендер может перезаписываться

При изменении состояния select Slim Select пересоздает DOM-элементы, поэтому любые прямые манипуляции с внутренним HTML могут быть потеряны.

Безопасность и XSS

Использование HTML в опциях открывает потенциальный риск внедрения скриптов. Поэтому критически важно учитывать источник данных.

Slim Select не выполняет полноценную санитизацию HTML, поэтому:

  • HTML должен формироваться на стороне приложения
  • пользовательский ввод нельзя вставлять напрямую в html
  • необходимо экранировать данные при необходимости

Пример небезопасного подхода:

{
  value: '1',
  text: userInput,
  html: `<div>${userInput}</div>`
}

Корректный подход:

function escapeHtml(str) {
  return str
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
}

{
  value: '1',
  text: escapeHtml(userInput),
  html: `<div>${escapeHtml(userInput)}</div>`
}

Комбинирование text и html

Наиболее стабильная модель использования:

  • text — для логики (поиск, значение, fallback)
  • html — для визуального отображения
{
  value: 'admin',
  text: 'Administrator',
  html: `<div class="role role--admin">
           <span class="badge">A</span>
           Administrator
         </div>`
}

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

Динамическое обновление HTML-опций

При изменении данных через API Slim Select HTML обновляется вместе с опциями:

const select = new SlimSelect({
  select: '#select',
  data: []
})

select.setData([
  {
    value: '1',
    text: 'New item',
    html: '<strong>New item</strong>'
  }
])

При каждом вызове setData происходит полная переразметка списка, включая HTML-узлы.

Поведение при отсутствии html-поля

Если html не задан, используется text:

{
  value: '1',
  text: '<b>Bold</b>'
}

Результат будет отображен как текст:

<b>Bold</b>

Это поведение предотвращает случайное выполнение HTML при отсутствии явного разрешения через html.

Практическая модель построения UI

HTML-опции часто используются для построения:

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

Пример тарифов:

{
  value: 'free',
  text: 'Free',
  html: `<div class="plan plan--free">
           <span>Free</span>
           <small>Basic features</small>
         </div>`
},
{
  value: 'pro',
  text: 'Pro',
  html: `<div class="plan plan--pro">
           <span>Pro</span>
           <small>Advanced tools</small>
           <span class="badge">Popular</span>
         </div>`
}

Такой подход превращает обычный <select> в полноценный компонент интерфейса с кастомной версткой и семантической логикой.