В Choices.js система шаблонов строится вокруг функций, возвращающих
строку HTML. Каждый шаблон в конфигурации templates может
быть задан как функция, получающая контекст элемента и возвращающая
разметку, которая будет вставлена в DOM. Такой подход позволяет
полностью контролировать отображение списка, выбранных элементов и
групп, не ограничиваясь стандартным представлением.
Ключевая особенность заключается в том, что шаблоны не являются статическими строками — они вычисляются динамически при каждом рендере, что открывает доступ к состоянию компонента и данным конкретного элемента.
Конфигурация templates передаётся при инициализации
экземпляра:
const choices = new Choices('#select', {
templates: {
item: function (classNames, data) {
return `<div class="${classNames.item}">${data.label}</div>`;
}
}
});
Каждая функция получает два основных аргумента:
classNames — объект с предопределёнными CSS-классами
Choices.jsdata — объект данных текущего элементаВ зависимости от типа шаблона могут добавляться дополнительные параметры.
Объект data является центральным элементом при
построении шаблонов. Он содержит всю информацию об опции или выбранном
элементе:
value — значение элементаlabel — отображаемый текстid — уникальный идентификаторdisabled — флаг недоступностиselected — состояние выбораactive — активный элемент в спискеcustomProperties — пользовательские данные, переданные
в опциюПример использования расширенного контекста:
{
value: 'ru',
label: 'Русский',
selected: true,
disabled: false,
customProperties: {
flag: '??',
region: 'EU'
}
}
Наиболее часто переопределяется шаблон choice,
отвечающий за отображение элементов выпадающего списка.
const choices = new Choices('#select', {
templates: {
choice: function (classNames, data) {
return `
<div class="${classNames.item} ${classNames.itemChoice}"
data-choice
data-id="${data.id}"
data-value="${data.value}"
${data.disabled ? 'aria-disabled="true"' : ''}>
<span class="label">${data.label}</span>
</div>
`;
}
}
});
Функциональный подход позволяет:
data-*Шаблон item используется для отображения выбранных
значений в поле ввода.
templates: {
item: function (classNames, data) {
return `
<div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : ''}"
data-item
data-id="${data.id}"
data-value="${data.value}">
<span class="value">${data.label}</span>
<button type="button" class="remove-button" data-button="remove-item">
×
</button>
</div>
`;
}
}
Здесь важно учитывать состояние элемента:
Функции позволяют внедрять условную логику прямо в генерацию HTML. Это один из ключевых механизмов кастомизации.
item: function (classNames, data) {
let icon = '';
if (data.customProperties && data.customProperties.icon) {
icon = `<span class="icon">${data.customProperties.icon}</span>`;
}
return `
<div class="${classNames.item}">
${icon}
<span>${data.label}</span>
</div>
`;
}
Такой подход применяется для:
Choices.js поддерживает группировку элементов, и для этого
используется шаблон group.
templates: {
group: function (classNames, data) {
return `
<div class="${classNames.group}">
<div class="${classNames.groupHeading}">
${data.label}
</div>
<div class="${classNames.groupList}">
</div>
</div>
`;
}
}
Контекст data в этом случае содержит:
label — название группыdisabled — состояние группыid — идентификаторФункциональный шаблон позволяет полностью менять структуру группировки, включая добавление дополнительных элементов, счетчиков или меток.
customProperties является расширяемым полем, которое
позволяет передавать произвольные данные в шаблон.
{
value: 'js',
label: 'JavaScript',
customProperties: {
level: 'advanced',
color: '#f7df1e'
}
}
Пример использования:
choice: function (classNames, data) {
return `
<div class="${classNames.item}" style="border-left: 3px solid ${data.customProperties.color}">
${data.label}
<small>${data.customProperties.level}</small>
</div>
`;
}
Это позволяет внедрять:
Поскольку шаблоны возвращают HTML-строки, важно учитывать
безопасность входных данных. Значения label и
value могут быть потенциально небезопасными, если они
поступают из внешних источников.
Рекомендуется использовать экранирование:
function escapeHtml(str) {
return String(str)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
Применение:
item: function (classNames, data) {
return `
<div class="${classNames.item}">
${escapeHtml(data.label)}
</div>
`;
}
Функции шаблонов вызываются при каждом изменении состояния компонента. Это позволяет учитывать:
selected)active)Пример изменения внешнего вида при активности:
choice: function (classNames, data) {
const activeClass = data.active ? classNames.highlightedState : '';
return `
<div class="${classNames.item} ${activeClass}">
${data.label}
</div>
`;
}
Объект classNames централизует все CSS-классы
библиотеки. Это позволяет не хардкодить строки классов и сохранять
совместимость с внутренней структурой Choices.js.
Пример использования:
templates: {
item: function (classNames, data) {
return `<div class="${classNames.item} ${classNames.itemSelectable}">
${data.label}
</div>`;
}
}
Основные преимущества:
Функциональные шаблоны позволяют выносить сложную логику отображения прямо в конфигурацию компонента. При этом сохраняется разделение:
Типовой паттерн:
function renderLabel(data) {
return data.disabled
? `<s>${data.label}</s>`
: data.label;
}
templates: {
choice: function (classNames, data) {
return `
<div class="${classNames.item}">
${renderLabel(data)}
</div>
`;
}
}
Несмотря на гибкость, существует ряд ограничений:
Оптимизация достигается за счёт:
При усложнении интерфейса шаблоны целесообразно разбивать на функции:
function renderIcon(icon) {
return icon ? `<span class="icon">${icon}</span>` : '';
}
function renderMeta(meta) {
return meta ? `<small>${meta}</small>` : '';
}
templates: {
choice: function (classNames, data) {
return `
<div class="${classNames.item}">
${renderIcon(data.customProperties?.icon)}
${data.label}
${renderMeta(data.customProperties?.meta)}
</div>
`;
}
}
Такой подход улучшает читаемость и упрощает сопровождение логики отображения.