Переход с Select2 на Tom Select затрагивает не только API, но и сам подход к построению компонента выбора. Несмотря на внешнюю схожесть, библиотеки имеют различную внутреннюю структуру, систему событий, модель расширения и принципы работы с DOM.
| Возможность | Select2 | Tom Select |
|---|---|---|
| Зависимость от jQuery | Требуется | Не требуется |
| Размер библиотеки | Больше | Легче |
| Архитектура | jQuery-плагин | Нативный ES6-класс |
| Плагины | Ограниченная система | Гибкая plugin API |
| Работа с данными | jQuery-ориентированная | Объектная модель |
| Поддержка TypeScript | Ограниченная | Более удобная |
| Кастомный рендеринг | Через шаблоны | Через render callbacks |
| Производительность | Ниже на больших списках | Выше |
Tom Select появился как современное развитие идей Selectize.js и ориентирован на отказ от jQuery, улучшенную производительность и более чистую архитектуру.
Одно из главных изменений при миграции — отказ от jQuery.
$('#users').select2({
placeholder: 'Выберите пользователя'
});
new TomSelect('#users', {
placeholder: 'Выберите пользователя'
});
Tom Select использует обычные CSS-селекторы и работает напрямую с DOM.
<link rel="stylesheet" href="select2.min.css">
<script src="jquery.min.js"></script>
<script src="select2.min.js"></script>
<link rel="stylesheet" href="tom-select.css">
<script src="tom-select.complete.min.js"></script>
В Tom Select отсутствует обязательная зависимость от jQuery, что значительно уменьшает общий размер frontend-бандла.
$('.tags').select2();
document.querySelectorAll('.tags').forEach((element) => {
new TomSelect(element);
});
В Tom Select экземпляр создаётся через конструктор класса.
const instance = $('#users').data('select2');
const select = new TomSelect('#users');
console.log(select);
Экземпляр хранится напрямую в переменной.
Также доступен через DOM:
const control = document.querySelector('#users').tomselect;
const value = $('#users').val();
const value = select.getValue();
$('#users').val('admin').trigger('change');
select.setValue('admin');
Tom Select автоматически инициирует необходимые обновления интерфейса.
<select id="skills" multiple>
$('#skills').select2();
<select id="skills" multiple>
new TomSelect('#skills');
Базовая логика работы остаётся схожей.
$('#country').select2({
placeholder: 'Страна'
});
new TomSelect('#country', {
placeholder: 'Страна'
});
Однако в Tom Select placeholder отображается иначе при multiple-режиме и зависит от состояния поля.
$('#users').select2({
ajax: {
url: '/api/users',
dataType: 'json',
delay: 250,
processResults: function(data) {
return {
results: data.items
};
}
}
});
new TomSelect('#users', {
load: function(query, callback) {
fetch(`/api/users?q=${query}`)
.then(response => response.json())
.then(data => {
callback(data.items);
})
.catch(() => {
callback();
});
}
});
| Select2 | Tom Select |
|---|---|
| Конфигурация через ajax | Используется load |
| Встроенный transport | Используется fetch/XHR вручную |
| processResults | callback(data) |
| jQuery AJAX | Любой HTTP-клиент |
Tom Select предоставляет более низкоуровневый контроль.
{
"results": [
{
"id": 1,
"text": "Admin"
}
]
}
[
{
"value": 1,
"text": "Admin"
}
]
Tom Select использует более простую структуру.
Если backend возвращает структуру Select2, можно настроить поля.
new TomSelect('#users', {
valueField: 'id',
labelField: 'text',
searchField: 'text',
load: function(query, callback) {
fetch('/api/users')
.then(r => r.json())
.then(data => {
callback(data.results);
});
}
});
Это позволяет не менять серверный API во время миграции.
$('#users').on('select2:select', function(e) {
console.log(e.params.data);
});
select.on('item_add', function(value) {
console.log(value);
});
| Select2 | Tom Select |
|---|---|
| select2:select | item_add |
| select2:unselect | item_remove |
| change | change |
| select2:open | dropdown_open |
| select2:close | dropdown_close |
$('#users').on('select2:select', function(e) {
console.log(e.params.data);
});
select.on('item_add', function(value, item) {
console.log(value);
console.log(item);
});
Tom Select передаёт аргументы напрямую, без обёртки event object.
$('#users').select2('destroy');
select.destroy();
После destroy() Tom Select полностью удаляет собственные обработчики и DOM-модификации.
$('#users').append(new Option('Admin', 1));
select.addOption({
value: 1,
text: 'Admin'
});
select.refreshOptions(false);
$('#users').val(1).trigger('change');
select.addItem(1);
select.removeItem(1);
$('#users').val(null).trigger('change');
select.clear();
$('#users').select2({
templateResult: function(user) {
return $(`
<div>
<strong>${user.text}</strong>
</div>
`);
}
});
new TomSelect('#users', {
render: {
option: function(data, escape) {
return `
<div>
<strong>${escape(data.text)}</strong>
</div>
`;
}
}
});
В Select2 экранирование часто делалось вручную или через jQuery.
Tom Select предоставляет встроенную функцию escape.
render: {
option: function(data, escape) {
return `
<div>${escape(data.text)}</div>
`;
}
}
Это особенно важно при отображении данных из API.
$('#tags').select2({
tags: true
});
new TomSelect('#tags', {
create: true
});
new TomSelect('#tags', {
create: function(input) {
return {
value: input,
text: input
};
}
});
matcher: function(params, data) {
return data;
}
searchField: ['name', 'email']
Tom Select делает акцент на конфигурации поисковых полей, а не на полном переопределении matcher.
maximumSelectionLength: 3
maxItems: 3
$('#users').prop('disabled', true);
select.disable();
Включение обратно:
select.enable();
<optgroup label="Backend">
<option>PHP</option>
</optgroup>
Поддержка optgroup сохраняется.
Также можно работать через JS-конфигурацию:
new TomSelect('#skills', {
optgroups: [
{
value: 'backend',
label: 'Backend'
}
],
options: [
{
value: 'php',
text: 'PHP',
optgroup: 'backend'
}
]
});
Select2 использует ограниченную систему расширения.
Tom Select поддерживает полноценные plugins.
new TomSelect('#tags', {
plugins: ['remove_button']
});
| Select2 | Tom Select |
|---|---|
| tags | create |
| tokenSeparators | delimiter |
| maximumSelectionLength | maxItems |
| templateResult | render.option |
| templateSelection | render.item |
| ajax | load |
| matcher | searchField/custom scoring |
| placeholder | placeholder |
tokenSeparators: [',']
delimiter: ','
minimumInputLength: 0
preload: true
new TomSelect('#users', {
preload: 'focus',
load: function(query, callback) {
fetch('/api/users')
.then(r => r.json())
.then(data => callback(data));
}
});
Select2 генерирует сложную структуру классов:
.select2-container
.select2-selection
.select2-results
Tom Select использует более простую структуру:
.ts-wrapper
.ts-control
.ts-dropdown
Наиболее частая проблема при переходе — старые стили Select2 продолжают влиять на новый компонент.
Рекомендуется:
.select2-container {
all: unset;
}
или полное удаление Select2 CSS.
theme: 'classic'
Tom Select использует обычные CSS-файлы тем.
<link rel="stylesheet" href="tom-select.bootstrap5.css">
Часто требовались сторонние темы.
Доступны готовые варианты:
tom-select.bootstrap4.css
tom-select.bootstrap5.css
Tom Select синхронизирует состояние с исходным select автоматически.
<select name="users[]" multiple>
При отправке формы данные отправляются стандартным образом.
$('#users').select2({
data: users
});
new TomSelect('#users', {
options: users
});
select.updateOption(1, {
value: 1,
text: 'Administrator'
});
select.clearOptions();
select.addOptions([
{ value: 1, text: 'Admin' },
{ value: 2, text: 'Editor' }
]);
Tom Select работает быстрее благодаря:
Особенно заметна разница при:
$('#users').select2('open');
select.open();
В Tom Select данные должны приводиться вручную.
load(query, callback) {
fetch('/api/users')
.then(r => r.json())
.then(data => {
callback(
data.results.map(item => ({
value: item.id,
text: item.text
}))
);
});
}
Select2 создаёт большое количество вложенных элементов.
Tom Select использует более компактную структуру.
Это влияет на:
$('#users').val(1).trigger('change');
select.setValue(1);
ajax: {
url: '/api/users'
}
load(query, callback) {
fetch('/api/users')
.then(r => r.json())
.then(callback);
}
templateResult
render.option
Удаляются:
.select2-container.select2-selection.select2-resultsДобавляются:
.ts-wrapper.ts-control.ts-dropdownИногда обе библиотеки работают одновременно.
if (window.TomSelect) {
new TomSelect('#users');
} else {
$('#users').select2();
}
Часто сначала переводятся:
Наиболее частые несовместимости:
| Проблема | Решение |
|---|---|
| results вместо массива | callback(data.results) |
| id/text поля | valueField/labelField |
| jQuery AJAX headers | fetch headers |
| pagination Select2 | собственная логика |
$('#users').select2({
placeholder: 'Пользователь',
ajax: {
url: '/api/users',
dataType: 'json'
},
templateResult: formatUser,
minimumInputLength: 2,
maximumSelectionLength: 5,
tags: true
});
new TomSelect('#users', {
placeholder: 'Пользователь',
maxItems: 5,
create: true,
preload: false,
load: function(query, callback) {
if (query.length < 2) {
return callback();
}
fetch(`/api/users?q=${query}`)
.then(r => r.json())
.then(data => callback(data.results))
.catch(() => callback());
},
render: {
option: function(data, escape) {
return `
<div>
${escape(data.text)}
</div>
`;
}
}
});