В библиотеке Choices.js визуальное представление выбранных элементов (selected items) отделено от логики хранения данных. Это позволяет гибко настраивать интерфейс: изменять структуру HTML, добавлять дополнительные элементы, управлять отображением текста, иконок, меток и интерактивных элементов внутри выбранных значений.
Шаблонизация выбранных элементов основана на наборе функций
callback, передаваемых в конфигурацию экземпляра. Каждая
функция отвечает за формирование DOM-структуры одного элемента. Наиболее
важной среди них является callbackOnCreateTemplates,
которая переопределяет стандартные шаблоны рендеринга.
Внутри Choices.js каждый выбранный элемент представляет собой объект, который преобразуется в DOM-узел. Этот процесс происходит через систему шаблонов, где ключевую роль играет функция генерации HTML-структуры.
Основная цепочка выглядит следующим образом:
Ключевое место занимает объект templates, который
переопределяется при инициализации.
Функция callbackOnCreateTemplates возвращает объект,
содержащий методы генерации UI-элементов. Каждый метод отвечает за
конкретный тип элемента, включая выбранные значения.
Структура:
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="choices__item-text">${data.label}</span>
</div>
`);
}
};
}
});
В данном примере шаблон item полностью заменяет
стандартный рендер выбранного элемента.
Каждый элемент, передаваемый в шаблон, содержит набор стандартных свойств:
id — уникальный идентификатор внутри спискаvalue — значение элементаlabel — отображаемый текстcustomProperties — пользовательские метаданныеactive — состояние активностиhighlighted — состояние подсветкиdisabled — недоступность элементаИспользование этих свойств позволяет строить динамические шаблоны, учитывающие состояние элемента без дополнительной логики.
Наиболее распространённый сценарий — расширение выбранного элемента дополнительными визуальными компонентами.
Пример добавления иконки и бейджа:
item: (classNames, data) => {
return template(`
<div class="${classNames.item}" data-item data-id="${data.id}" data-value="${data.value}">
<span class="choices__icon"></span>
<span class="choices__label">${data.label}</span>
<span class="choices__badge">${data.customProperties?.badge || ''}</span>
</div>
`);
}
В данном случае структура становится составной: текстовая часть отделена от вспомогательных визуальных элементов, что облегчает управление стилями.
По умолчанию каждый выбранный элемент содержит кнопку удаления. Шаблон этой кнопки также может быть переопределён через объект шаблонов.
removeItemButton: (classNames) => {
return template(`
<button type="button" class="${classNames.button}" data-button>
<svg class="icon-close"></svg>
</button>
`);
}
Изменение структуры кнопки удаления позволяет интегрировать собственные иконки или поведение, включая анимации или подтверждение действия.
Поле customProperties предоставляет возможность
расширять стандартную модель данных. Оно особенно полезно при работе с
тегами, пользователями, категориями или сложными объектами.
Пример использования:
item: (classNames, data) => {
const color = data.customProperties?.color || 'default';
return template(`
<div class="${classNames.item} item--${color}" data-value="${data.value}">
<span>${data.label}</span>
</div>
`);
}
Таким образом можно внедрять семантические стили без изменения логики компонента.
Состояния элементов напрямую влияют на их визуальное представление. Наиболее важные состояния:
Пример обработки:
item: (classNames, data) => {
const stateClass = data.disabled
? classNames.itemDisabled
: data.highlighted
? classNames.highlightedState
: classNames.itemSelectable;
return template(`
<div class="${classNames.item} ${stateClass}" data-value="${data.value}">
${data.label}
</div>
`);
}
Такой подход позволяет централизованно управлять визуальной логикой без изменения CSS-структуры библиотеки.
Choices.js допускает создание сложных шаблонов, где выбранный элемент содержит дополнительные интерактивные блоки.
Пример:
item: (classNames, data) => {
return template(`
<div class="${classNames.item}">
<div class="item-content">
<span class="title">${data.label}</span>
<span class="meta">${data.customProperties?.description || ''}</span>
</div>
<div class="item-actions">
<button data-action="edit"></button>
</div>
</div>
`);
}
Подобная структура используется в интерфейсах, где выбранные элементы выступают как управляемые сущности, а не просто значения.
При работе с API или динамическими источниками данных шаблон должен учитывать возможное отсутствие части свойств.
item: (classNames, data) => {
const label = data.label || data.value || 'undefined';
return template(`
<div class="${classNames.item}" data-value="${data.value}">
${label}
</div>
`);
}
Такая защита предотвращает некорректный рендеринг при неполных данных.
Библиотека предоставляет набор системных классов, которые автоматически применяются к элементам:
choices__itemchoices__item--selectablechoices__item--disabledchoices__item--highlightedИспользование этих классов внутри шаблонов обеспечивает совместимость с внутренней логикой управления состояниями.
Шаблон выбранного элемента может включать атрибуты для последующей обработки событий:
item: (classNames, data) => {
return template(`
<div class="${classNames.item}" data-action="select" data-id="${data.id}">
${data.label}
</div>
`);
}
Это позволяет реализовать внешние обработчики кликов без изменения внутренней логики Choices.js.
При глубокой кастомизации следует учитывать архитектурные ограничения:
Поэтому сложные шаблоны целесообразно оптимизировать, минимизируя количество вычислений внутри функций рендера.
Для масштабных проектов шаблоны часто выносятся в отдельные функции:
function createItemTemplate(template, classNames) {
return (data) => {
return template(`
<div class="${classNames.item}">
${data.label}
</div>
`);
};
}
Такой подход позволяет унифицировать отображение выбранных элементов в разных инстансах компонента и упростить поддержку UI.