Работа с изображениями в выпадающих списках на базе Choices.js опирается на расширение стандартного шаблона рендеринга элементов и подключение пользовательских полей данных к объектам выбора. Библиотека изначально ориентирована на текстовые значения, однако архитектура позволяет полностью переопределять представление как выбранных элементов, так и элементов списка.
Основной принцип реализации селектора с изображениями заключается в том, что каждый элемент данных дополняется URL изображения, а затем отображение элемента формируется через пользовательские шаблоны.
Каждый элемент списка расширяется дополнительным полем, которое хранит путь к изображению. Стандартный формат данных для Choices.js допускает произвольные свойства:
const items = [
{
value: 'paris',
label: 'Париж',
image: 'https://example.com/images/paris.jpg'
},
{
value: 'tokyo',
label: 'Токио',
image: 'https://example.com/images/tokyo.jpg'
},
{
value: 'ny',
label: 'Нью-Йорк',
image: 'https://example.com/images/ny.jpg'
}
];
Ключевой момент заключается в том, что библиотека не использует поле
image автоматически — оно применяется исключительно в
кастомных шаблонах отображения.
Подключение начинается с создания экземпляра селектора на основе
существующего select элемента:
const element = document.querySelector('#city-select');
const choices = new Choices(element, {
searchEnabled: true,
itemSelectText: '',
shouldSort: false
});
На этом этапе список остаётся стандартным текстовым, но уже готов к расширению визуального слоя.
Для вывода изображений используется механизм
callbackOnCreateTemplates, который позволяет полностью
заменить шаблоны элементов списка и выбранных значений.
const choices = new Choices(element, {
searchEnabled: true,
itemSelectText: '',
shouldSort: false,
callbackOnCreateTemplates: function (template) {
return {
choice: (classNames, data) => {
return template(`
<div class="${classNames.item} ${classNames.itemChoice}"
data-select-text="${this.config.itemSelectText}"
data-choice
data-id="${data.id}"
data-value="${data.value}"
${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'}>
<img class="choice-image" src="${data.customProperties.image}" alt="${data.label}">
<span class="choice-label">${data.label}</span>
</div>
`);
},
item: (classNames, data) => {
return template(`
<div class="${classNames.item} ${classNames.itemSelectable}"
data-item
data-id="${data.id}"
data-value="${data.value}">
<img class="item-image" src="${data.customProperties.image}" alt="${data.label}">
<span class="item-label">${data.label}</span>
</div>
`);
}
};
}
});
Choices.js безопасно переносит дополнительные поля через
customProperties. Поэтому изображения следует передавать
именно через этот объект:
const items = [
{
value: 'paris',
label: 'Париж',
customProperties: {
image: 'https://example.com/images/paris.jpg'
}
},
{
value: 'tokyo',
label: 'Токио',
customProperties: {
image: 'https://example.com/images/tokyo.jpg'
}
}
];
Такой подход предотвращает конфликты с внутренними полями библиотеки и обеспечивает стабильность при обновлениях.
После добавления <img> элементов требуется
определить поведение визуальных компонентов через CSS.
.choices__list--dropdown .choice-image {
width: 32px;
height: 32px;
object-fit: cover;
border-radius: 6px;
margin-right: 10px;
vertical-align: middle;
}
.choices__list--single .item-image {
width: 24px;
height: 24px;
object-fit: cover;
border-radius: 4px;
margin-right: 8px;
vertical-align: middle;
}
Для корректного отображения изображения и текста применяется flex-модель:
.choices__item--choice {
display: flex;
align-items: center;
gap: 10px;
}
.choices__item--selectable {
display: flex;
align-items: center;
gap: 8px;
}
При динамическом получении данных через API структура остаётся аналогичной, однако добавляется этап трансформации ответа.
fetch('/api/cities')
.then(response => response.json())
.then(data => {
const formatted = data.map(city => ({
value: city.id,
label: city.name,
customProperties: {
image: city.image_url
}
}));
choices.setChoices(formatted, 'value', 'label', true);
});
Ключевой момент заключается в сохранении единого формата данных независимо от источника.
При использовании изображений часто требуется отключение сортировки, чтобы сохранить визуальную целостность интерфейса:
shouldSort: false,
searchEnabled: true,
searchFields: ['label']
Поиск работает исключительно по текстовому полю label,
изображения не участвуют в индексации.
При большом количестве элементов список может содержать десятки или сотни изображений. В таких случаях применяются следующие подходы:
<img loading="lazy" src="${data.customProperties.image}" alt="${data.label}">
<img src="${data.customProperties.image || '/placeholder.png'}">
const preload = (items) => {
items.forEach(item => {
const img = new Image();
img.src = item.customProperties.image;
});
};
При отсутствии изображения важно предотвращать поломку интерфейса:
const safeImage = data.customProperties?.image || '/default.png';
Или через CSS-фоллбек:
img {
background-color: #f0f0f0;
}
Choices.js допускает расширение логики до категорий с изображениями, где каждый элемент дополнительно группируется.
const grouped = [
{
label: 'Европа',
choices: [
{
value: 'paris',
label: 'Париж',
customProperties: {
image: 'https://example.com/paris.jpg'
}
}
]
}
];
Дополнительные визуальные эффекты часто добавляются через модификацию шаблонов:
Пример:
<span class="choice-overlay"></span>
.choice-overlay {
position: absolute;
inset: 0;
opacity: 0;
transition: opacity 0.2s;
}
.choices__item--choice:hover .choice-overlay {
opacity: 0.1;
}
Использование изображений внутри Choices.js увеличивает нагрузку на DOM, так как каждый элемент создаёт дополнительные узлы с медиа-контентом. При больших списках наблюдаются следующие эффекты:
Для компенсации применяется ограничение количества элементов:
maxItemCount: 10,
renderChoiceLimit: 20
В расширенных интерфейсах изображения часто сочетаются с тегами или дополнительными метаданными:
{
value: 'tokyo',
label: 'Токио',
customProperties: {
image: 'https://example.com/tokyo.jpg',
subtitle: 'Япония'
}
}
Рендер:
<div>
<img src="...">
<div>
<div>Токио</div>
<small>Япония</small>
</div>
</div>
При обновлении Choices.js важно учитывать, что внутренние классы и
структура template могут изменяться. Поэтому все визуальные
расширения должны опираться на:
classNames, передаваемые библиотекойdata-* атрибутыcustomPropertiesЖёсткая привязка к HTML-структуре без использования этих механизмов приводит к поломке при обновлениях.
Общая схема работы состоит из трёх слоёв:
customProperties.imagecallbackOnCreateTemplates для
рендераТакая модель позволяет превратить стандартный текстовый селектор в визуальный компонент с карточками, сохраняя при этом всю функциональность Choices.js, включая поиск, выбор и управление состоянием.