Шаблон для опций

В Choices.js визуальное представление элементов списка и выбранных значений полностью отделено от логики данных. Это достигается через систему шаблонов, позволяющую переопределять разметку для каждого типа сущности: вариантов выбора, выбранных элементов, групп, сообщений состояния и элементов поиска.

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


Основные типы шаблонов

Choices.js использует набор шаблонов, каждый из которых отвечает за конкретный элемент интерфейса:

  • choice — элемент списка доступных вариантов
  • item — элемент выбранного значения
  • group — заголовок группы (optgroup)
  • input — строка ввода поиска
  • dropdown — контейнер выпадающего списка
  • container — корневой элемент компонента
  • notice — сообщения состояния (нет результатов, загрузка и т.д.)

Каждый шаблон может быть переопределен через конфигурацию callbackOnCreateTemplates.


Механизм callbackOnCreateTemplates

Ключевой точкой расширения визуального слоя является функция:

const choices = new Choices(element, {
  callbackOnCreateTemplates: function (template) {
    return {
      item: (classNames, data) => {
        return template(`
          <div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : ''}" 
               data-item 
               data-id="${data.id}" 
               data-value="${data.value}">
            <span class="custom-item-text">${data.label}</span>
          </div>
        `);
      },

      choice: (classNames, data) => {
        return template(`
          <div class="${classNames.item} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : ''}"
               data-choice
               data-id="${data.id}"
               data-value="${data.value}">
            <span class="custom-choice-text">${data.label}</span>
          </div>
        `);
      }
    };
  }
});

Функция получает объект template, который преобразует строку HTML в DOM-элемент с необходимой внутренней обработкой библиотеки.


Шаблон элемента выбора (choice)

Элемент выбора — это базовая единица списка. Он отображается в выпадающем меню и представляет один возможный вариант.

Структура данных choice

Каждый choice содержит:

  • id — внутренний идентификатор
  • value — значение, отправляемое при выборе
  • label — отображаемый текст
  • disabled — состояние недоступности
  • selected — выбран ли элемент
  • active — доступен ли для выбора после фильтрации

Переопределение шаблона choice

choice: (classNames, data) => {
  const status = data.disabled ? 'disabled' : 'active';

  return template(`
    <div class="${classNames.item} ${classNames.itemChoice} status-${status}"
         role="option"
         data-choice
         data-id="${data.id}"
         data-value="${data.value}">
      
      <div class="choice-main">
        <strong class="choice-label">${data.label}</strong>
      </div>

      ${data.customProperties?.description
        ? `<div class="choice-description">${data.customProperties.description}</div>`
        : ''}
    </div>
  `);
}

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

Choices.js позволяет добавлять произвольные данные в customProperties, что расширяет шаблон:

choices.setChoices([
  {
    value: 'js',
    label: 'JavaScript',
    customProperties: {
      description: 'Язык программирования для веба'
    }
  }
]);

Шаблон выбранного элемента (item)

Элемент item отображается внутри поля выбора после того, как опция была выбрана.

Поведение item

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

Переопределение item

item: (classNames, data) => {
  return template(`
    <div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : ''}"
         data-item
         data-id="${data.id}"
         data-value="${data.value}">
      
      <span class="item-label">${data.label}</span>

      <button type="button"
              class="item-remove"
              data-button="remove">
        ×
      </button>
    </div>
  `);
}

Особенности работы с item

  • data.highlighted используется при навигации с клавиатуры
  • data.active может влиять на возможность удаления
  • кнопка удаления должна иметь data-button="remove" для интеграции с логикой библиотеки

Шаблон групп (optgroup)

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

Структура group

  • label — название группы
  • id — идентификатор
  • disabled — блокировка всей группы

Пример кастомного group шаблона

group: (classNames, data) => {
  return template(`
    <div class="${classNames.group}"
         data-group
         data-id="${data.id}">
      
      <div class="group-header">
        <span class="group-title">${data.label}</span>
      </div>

      <div class="group-children">
        ${data.children}
      </div>
    </div>
  `);
}

Особенности рендеринга групп

Choices.js уже формирует вложенные children, поэтому шаблон группы отвечает только за оболочку. Важно сохранять контейнер для дочерних элементов, иначе список не будет отображаться корректно.


Шаблон контейнера и dropdown

Контейнер определяет общую структуру компонента, включая input, список и состояния.

container

containerOuter: (classNames, data) => {
  return template(`
    <div class="${classNames.containerOuter}"
         data-type="${data.type}">
      ${data.input}
      ${data.dropdown}
    </div>
  `);
}
dropdown: (classNames) => {
  return template(`
    <div class="${classNames.list}"
         aria-expanded="false">
      <div class="${classNames.listInner}">
        <!-- choices -->
      </div>
    </div>
  `);
}

Шаблоны состояния (notice)

Notice используется для отображения системных сообщений:

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

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

notice: (classNames, message) => {
  return template(`
    <div class="${classNames.item} notice"
         role="alert">
      <span class="notice-text">${message}</span>
    </div>
  `);
}

Типы сообщений

Choices.js автоматически передает сообщения:

  • No results found
  • Loading...
  • Press to select

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


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

Функция template внутри callbackOnCreateTemplates выполняет несколько задач:

  • парсит HTML строку
  • создает DOM-элемент
  • применяет внутренние классы Choices.js
  • обеспечивает безопасное связывание с событиями библиотеки

Важно использовать именно template, а не innerHTML, так как библиотека ожидает специфическую структуру элементов.


Динамическое формирование шаблонов

Шаблоны могут зависеть от состояния данных:

choice: (classNames, data) => {
  const isVIP = data.customProperties?.vip;

  return template(`
    <div class="${classNames.item} ${isVIP ? 'vip' : ''}"
         data-choice
         data-value="${data.value}">
      ${isVIP ? '<span class="badge">VIP</span>' : ''}
      ${data.label}
    </div>
  `);
}

Такая модель позволяет:

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

Ограничения шаблонной системы

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

  • нельзя изменять порядок внутренних событий Choices.js
  • нельзя удалять обязательные data-атрибуты (data-choice, data-item)
  • нельзя полностью заменять контейнерную логику без риска поломки навигации
  • виртуализация и фильтрация управляются внутренним движком, а не шаблонами

Практика комбинирования шаблонов

Часто шаблоны используются совместно для создания единого UI:

  • choice формирует карточки в списке
  • item повторяет визуальный стиль выбранных элементов
  • group добавляет логическую структуру
  • notice унифицирует состояние интерфейса

Единообразие достигается через общие CSS-классы и согласованную структуру DOM.


Интеграция с CSS-моделями

Шаблоны тесно связаны с классами из classNames, которые предоставляет библиотека:

  • item
  • itemChoice
  • itemDisabled
  • highlightedState
  • list
  • group

Эти классы рекомендуется не заменять вручную, а расширять, сохраняя внутреннюю логику поведения.


Расширенные сценарии использования

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

  • карточки с изображениями в выпадающем списке
  • асинхронные превью данных
  • статусные индикаторы (онлайн/оффлайн, активен/архив)
  • мультимедийные элементы внутри choice
  • кастомные формы выбора (например, с чекбоксами внутри choice)

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

choice: (classNames, data) => {
  return template(`
    <div class="${classNames.item} custom-card"
         data-choice
         data-value="${data.value}">
      
      <img src="${data.customProperties?.icon}" class="icon" />
      <div class="content">
        <div class="title">${data.label}</div>
        <div class="subtitle">${data.customProperties?.subtitle}</div>
      </div>

    </div>
  `);
}