Slim Select позволяет расширять стандартный
<select> за счёт кастомных атрибутов, которые
становятся основой для сложных интерфейсов: иконок, меток, статусов,
метаданных, поведения поиска и динамического рендеринга. Основной
механизм расширения строится вокруг data-* атрибутов HTML и
пользовательских полей в объектах данных, которые Slim Select
прокидывает в шаблоны отображения.
Базовый HTML остаётся совместимым со стандартной разметкой, при этом
дополнительные данные добавляются через data-*:
<select id="country-select">
<option value="kz" data-code="KZ" data-flag="??" data-region="asia">
Kazakhstan
</option>
<option value="de" data-code="DE" data-flag="??" data-region="europe">
Germany
</option>
<option value="jp" data-code="JP" data-flag="??" data-region="asia">
Japan
</option>
</select>
Каждый атрибут, начинающийся с data-, автоматически
попадает в dataset DOM-элемента. Slim Select не игнорирует
эти данные, а передаёт их в объект опции при построении списка.
Инициализация:
new SlimSelect({
select: '#country-select'
});
На этом уровне Slim Select уже умеет отображать стандартные значения, но ключевая часть начинается при кастомизации рендера.
Каждая опция в Slim Select получает доступ к исходному DOM-элементу. Через него извлекаются атрибуты:
new SlimSelect({
select: '#country-select',
render: {
option: (data) => {
const option = data.option;
const flag = option.dataset.flag;
const code = option.dataset.code;
const region = option.dataset.region;
return `
<div class="option-item">
<span class="flag">${flag}</span>
<span class="label">${data.text}</span>
<span class="meta">${code} · ${region}</span>
</div>
`;
}
}
});
Здесь data.option — это DOM <option>,
а dataset содержит все кастомные атрибуты. Такой подход
превращает обычный select в структурированный источник данных.
Slim Select поддерживает не только DOM-инициализацию, но и
программное создание списка через data. В этом случае
кастомные атрибуты задаются как поля объекта:
new SlimSelect({
select: '#country-select',
data: [
{
text: 'Kazakhstan',
value: 'kz',
flag: '??',
code: 'KZ',
region: 'asia'
},
{
text: 'Germany',
value: 'de',
flag: '??',
code: 'DE',
region: 'europe'
}
],
render: {
option: (item) => {
return `
<div>
<strong>${item.text}</strong>
<small>${item.code} (${item.region})</small>
</div>
`;
}
}
});
В этом режиме item уже содержит все дополнительные поля
напрямую, без необходимости обращаться к dataset.
Кастомные атрибуты используются как слой данных, не влияющий на значение формы. Важно различать:
value — отправляется в формуtext — отображается по умолчаниюdata-* или дополнительные свойства
объекта) — используются только для UI/логикиПример:
{
text: 'Tokyo',
value: 'tokyo',
timezone: 'Asia/Tokyo',
population: 13960000,
status: 'active'
}
Отображение может учитывать статус:
render: {
option: (item) => {
const statusClass = item.status === 'active' ? 'is-active' : 'is-disabled';
return `
<div class="${statusClass}">
${item.text}
<span>${item.timezone}</span>
</div>
`;
}
}
Slim Select использует встроенный поиск по text, однако
кастомные поля позволяют расширить поведение через фильтрацию данных до
инициализации или через трансформацию массива.
Пример предобработки:
const rawData = [
{ text: 'Almaty', value: 'almaty', tags: ['city', 'kazakhstan'] },
{ text: 'Berlin', value: 'berlin', tags: ['city', 'germany'] }
];
new SlimSelect({
select: '#city-select',
data: rawData,
searchFilter: (opt, search) => {
const inText = opt.text.toLowerCase().includes(search.toLowerCase());
const inTags = opt.tags?.some(t => t.includes(search.toLowerCase()));
return inText || inTags;
}
});
Здесь кастомный атрибут tags расширяет стандартную
поисковую модель.
При использовании чистого <select> Slim Select не
требует изменения структуры, но кастомные атрибуты становятся важным
источником метаданных:
<option value="dev" data-role="developer" data-level="senior">
Developer
</option>
В render-функции:
render: {
option: (data) => {
const el = data.option;
return `
<div>
<span>${data.text}</span>
<span>${el.dataset.role}</span>
<span>${el.dataset.level}</span>
</div>
`;
}
}
Этот подход особенно полезен при интеграции с серверным рендерингом, где данные уже вложены в HTML.
При использовании <optgroup> кастомные атрибуты
могут применяться как к группам, так и к опциям внутри них:
<optgroup label="Europe" data-continent="EU">
<option value="de" data-population="83000000">Germany</option>
<option value="fr" data-population="67000000">France</option>
</optgroup>
Рендер группы:
render: {
optgroup: (group) => {
return `
<div class="group-header">
${group.label}
<small>${group.element.dataset.continent}</small>
</div>
`;
}
}
Таким образом метаданные уровня группы становятся частью UI.
При изменении данных через JavaScript кастомные поля сохраняются, если они передаются явно:
const slim = new SlimSelect({
select: '#country-select'
});
slim.setData([
{
text: 'Kazakhstan',
value: 'kz',
flag: '??',
region: 'asia'
}
]);
При обновлении важно сохранять структуру, иначе кастомные атрибуты будут потеряны.
Кастомные атрибуты часто используются для управления состоянием:
disabledlockedfeaturedhiddenПример:
{
text: 'Premium Plan',
value: 'premium',
featured: true,
locked: false
}
Рендер:
render: {
option: (item) => {
const featured = item.featured ? 'featured' : '';
return `
<div class="plan ${featured}">
${item.text}
</div>
`;
}
}
При загрузке данных с сервера кастомные атрибуты часто приходят как расширенные поля JSON:
{
"text": "USD",
"value": "usd",
"symbol": "$",
"precision": 2,
"market": "forex"
}
В Slim Select такие поля напрямую становятся частью объекта
item, что позволяет строить сложные интерфейсы без
промежуточных преобразований.
render: {
option: (item) => {
return `
<div>
${item.symbol} ${item.text}
<span>${item.market}</span>
</div>
`;
}
}
Кастомные атрибуты не участвуют в стандартной сериализации формы.
Отправляется только value. Поэтому любые дополнительные
данные должны обрабатываться отдельно при отправке:
form.addEventListener('submit', (e) => {
const value = slim.getSelected();
const enriched = value.map(v => ({
value: v,
meta: slim.getData().find(i => i.value === v)
}));
console.log(enriched);
});
Это позволяет восстановить контекст, связанный с кастомными полями.
Часто используется паттерн «обогащённой опции», где каждая запись содержит:
{
text: 'Enterprise',
value: 'enterprise',
badge: 'VIP',
color: 'gold',
limits: {
users: 1000,
storage: '1TB'
}
}
Такая структура позволяет Slim Select выступать не просто UI-компонентом, а слоем представления над полноценной моделью данных.