В основе интеграции с HTML-формами Tom Select опирается на
стандартный <select> элемент и его
<option> узлы. Любые data-* атрибуты,
добавленные к <option>, автоматически попадают в
объект данных элемента и становятся доступны внутри экземпляра
селекта.
Каждый <option> рассматривается не только как пара
value → text, но и как контейнер расширенных метаданных.
Это позволяет хранить произвольные поля прямо в DOM без необходимости
отдельного источника данных.
<select id="countries">
<option value="kz" data-code="KZ" data-population="19000000">
Kazakhstan
</option>
<option value="de" data-code="DE" data-population="83000000">
Germany
</option>
</select>
При инициализации Tom Select извлекает:
value → идентификаторdata-* → дополнительные поля объектаПри парсинге DOM каждый <option> конвертируется в
объект вида:
{
value: "kz",
text: "Kazakhstan",
code: "KZ",
population: "19000000"
}
Все атрибуты data-code, data-population
становятся свойствами объекта без префикса data-.
Важно учитывать, что значения всегда приходят строками, даже если в HTML они выглядят как числа.
После инициализации доступ к расширенным полям осуществляется через внутренние структуры Tom Select.
const select = new TomSelect("#countries");
const item = select.options["kz"];
console.log(item.code); // "KZ"
console.log(item.population); // "19000000"
Каждая запись хранится в select.options, где ключ — это
value, а значение — полный объект с метаданными.
Одно из ключевых применений data-атрибутов — формирование
пользовательского интерфейса через render.
new TomSelect("#countries", {
render: {
option: function(data, escape) {
return `
<div>
<span>${escape(data.text)}</span>
<small>${escape(data.code)}</small>
</div>
`;
},
item: function(data, escape) {
return `<div>${escape(data.text)} (${escape(data.code)})</div>`;
}
}
});
Здесь data.code и любые другие data-* поля
становятся частью шаблона без дополнительной обработки.
При добавлении новых элементов через API можно передавать дополнительные поля вручную:
select.addOption({
value: "fr",
text: "France",
code: "FR",
population: 67000000
});
Tom Select не ограничивает структуру объекта, поэтому любые поля
сохраняются в options.
При выборе элемента возвращается только value, однако
полный объект доступен через API:
select.on("change", (value) => {
const data = select.options[value];
console.log(data);
});
Это делает data-атрибуты основным способом расширения модели данных без изменения формы.
HTML не хранит типы, поэтому:
<option value="1" data-active="true" data-score="42">
будет преобразовано в:
{
value: "1",
active: "true",
score: "42"
}
Для восстановления типов требуется постобработка:
const normalize = (item) => ({
...item,
active: item.active === "true",
score: Number(item.score)
});
Tom Select поддерживает передачу метаданных и на уровне групп.
<optgroup label="Europe" data-region="EU">
<option value="de">Germany</option>
<option value="fr">France</option>
</optgroup>
При парсинге optgroup также может содержать
дополнительные свойства:
{
label: "Europe",
region: "EU"
}
Это позволяет реализовать фильтрацию и визуальную сегментацию интерфейса.
При использовании удалённых источников данных структура аналогична HTML-версии:
new TomSelect("#search", {
load: function(query, callback) {
fetch(`/api?q=${query}`)
.then(res => res.json())
.then(data => {
callback(data.items);
});
}
});
Каждый объект в items может содержать любые поля:
{
"value": "kz",
"text": "Kazakhstan",
"code": "KZ",
"continent": "Asia"
}
Эти поля становятся доступными так же, как и из
data-*.
Хотя Tom Select преобразует data-* в объект, исходный
DOM сохраняется и может быть получен:
const option = document.querySelector("#countries option[value='kz']");
console.log(option.dataset.code);
console.log(option.dataset.population);
dataset используется как промежуточный слой между HTML и
внутренней моделью данных.
Изменение DOM напрямую не всегда синхронизируется автоматически. Например:
const option = document.querySelector("option[value='kz']");
option.dataset.code = "KAZ";
Tom Select не обновит options автоматически. Требуется
явное обновление:
select.updateOption("kz", {
...select.options["kz"],
code: "KAZ"
});
Расширенные поля часто используются для кастомной логики поиска:
new TomSelect("#countries", {
score: function(search) {
return function(item) {
let score = 1;
if (item.code && item.code.includes(search.toUpperCase())) {
score += 10;
}
if (item.text.toLowerCase().includes(search.toLowerCase())) {
score += 5;
}
return score;
};
}
});
Здесь code — поле, полученное из data-code,
становится частью алгоритма ранжирования.
При включённой опции создания элементов (create: true)
можно автоматически добавлять дополнительные поля:
new TomSelect("#countries", {
create: function(input) {
return {
value: input.toLowerCase(),
text: input,
created: true
};
}
});
Добавленное поле created работает как маркер,
аналогичный data-*.
При получении значений через getValue() возвращается
только value, но расширенные данные можно восстановить
вручную:
const values = select.getValue();
const fullData = values.map(v => select.options[v]);
Это критично при отправке форм, где требуется передавать не только ID, но и связанные метаданные.
Несмотря на гибкость, модель имеет ряд особенностей:
data-* значения строковые по умолчаниюДля хранения сложных объектов применяется подход:
<option value="kz" data-meta='{"code":"KZ","population":19000000}'>
с последующим парсингом:
const meta = JSON.parse(item.meta);
Использование data-атрибутов в Tom Select формирует устойчивый паттерн:
Такая структура позволяет использовать компонент как легковесный
data-layer поверх стандартного <select> без
необходимости внедрения внешнего state-менеджера