Библиотека Tom Select появилась как современное развитие идей Selectize.js. Несмотря на внешнюю схожесть API, между ними существуют важные различия в архитектуре, зависимостях, механизмах рендеринга и расширяемости.
Selectize исторически опирался на jQuery и внутренние плагины jQuery-подобного типа. Tom Sel ect полностью избавлен от зависимости на jQuery и работает как самостоятельная ES-библиотека.
| Область | Selectize | Tom Select |
|---|---|---|
| Зависимости | Требуется jQuery | Не требует jQuery |
| Архитектура | Старый подход | Современный модульный код |
| Сборка | Ограниченная | ESM, CommonJS |
| Плагины | Старый формат | Современная система |
| Типизация | Отсутствует | Поддержка TypeScript |
| Производительность | Ниже | Выше |
| Поддержка | Практически остановлена | Активно развивается |
Самое заметное изменение при миграции — отказ от jQuery.
$('#skills').selectize({
create: true,
maxItems: 5
});
new TomSelect('#skills', {
create: true,
maxItems: 5
});
Tom Sel ect использует обычные CSS-селекторы и DOM API.
$('#skills')[0].selectize.on('change', function(value) {
console.log(value);
});
const control = new TomSelect('#skills');
control.on('change', (value) => {
console.log(value);
});
<link rel="stylesheet" href="selectize.css">
<script src="jquery.js"></script>
<script src="selectize.js"></script>
<link rel="stylesheet" href="tom-select.css">
<script src="tom-select.complete.min.js"></script>
npm install selectize
npm install tom-select
Tom Sel ect поддерживает современный импорт.
import TomSelect fr om 'tom-select';
new TomSelect('#users');
Импорт стилей:
import 'tom-select/dist/css/tom-select.css';
const selectize = $('#sel ect')[0].selectize;
const sel ect = new TomSelect('#sel ect');
Экземпляр создаётся напрямую через конструктор.
const control = $('#sel ect')[0].selectize;
const element = document.querySelector('#select');
const control = new TomSelect(element);
Большая часть API совместима, однако существуют отличия.
selectize.addOption({
value: 1,
text: 'JavaScript'
});
control.addOption({
value: 1,
text: 'JavaScript'
});
Сигнатура практически идентична.
selectize.refreshOptions();
control.refreshOptions(false);
Tom Select требует более явного управления поведением.
control.clear();
Метод полностью совместим.
selectize.destroy();
control.destroy();
Однако Tom Select корректнее освобождает обработчики событий и DOM-ссылки.
Selectize использовал старую архитектуру плагинов.
Selectize.define('custom_plugin', function(options) {
// plugin code
});
TomSelect.define('custom_plugin', function(options) {
// plugin code
});
Синтаксис похож, но внутренняя архитектура изменилась.
$('#select').selectize({
plugins: ['remove_button']
});
new TomSelect('#select', {
plugins: ['remove_button']
});
Tom Select более строго относится к HTML-рендерингу.
render: {
option: function(item, escape) {
return '<div>' + item.text + '</div>';
}
}
render: {
option(data, escape) {
return `<div>${escape(data.text)}</div>`;
}
}
Tom Select настоятельно требует экранирования пользовательских данных
через escape().
В старых проектах на Selectize часто встречается небезопасный код:
render: {
option(item) {
return `<div>${item.name}</div>`;
}
}
При миграции необходимо обязательно внедрять экранирование.
render: {
option(item, escape) {
return `<div>${escape(item.name)}</div>`;
}
}
load: function(query, callback) {
$.ajax({
url: '/search',
success: callback
});
}
load(query, callback) {
fetch(`/search?q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => callback(data))
.catch(() => callback());
}
Tom Select ориентирован на Fetch API и современные Promise.
Tom Select лучше работает с асинхронными источниками.
new TomSelect('#users', {
loadThrottle: 300,
load(query, callback) {
if (!query.length) {
return callback();
}
fetch(`/api/users?q=${query}`)
.then(res => res.json())
.then(json => callback(json.items))
.catch(() => callback());
}
});
Некоторые CSS-классы отличаются.
| Selectize | Tom Select |
|---|---|
.selectize-control |
.ts-wrapper |
.selectize-input |
.ts-control |
.selectize-dropdown |
.ts-dropdown |
После миграции старые стили могут перестать работать.
.selectize-input {
border-radius: 4px;
}
.ts-control {
border-radius: 4px;
}
Tom Select использует другую внутреннюю структуру контейнеров.
selectize.onItem Add = function(value) {
console.log(value);
};
control.on('item_add', (value) => {
console.log(value);
});
| Событие | Назначение |
|---|---|
change |
Изменение значения |
item_add |
Добавление элемента |
item_remove |
Удаление элемента |
dropdown_open |
Открытие списка |
dropdown_close |
Закрытие списка |
type |
Ввод текста |
create: true
create: true
Поведение совместимо, но Tom Select строже обрабатывает новые элементы.
new TomSelect('#tags', {
create(input) {
return {
value: input,
text: input
};
}
});
Tom Select использует улучшенный движок поиска.
new TomSelect('#users', {
searchField: ['name', 'email']
});
sortField: 'text'
sortField: [
{
field: 'text',
direction: 'asc'
}
]
Tom Select поддерживает более гибкую конфигурацию сортировки.
$('#select').selectize({
valueField: 'id',
labelField: 'name',
searchField: 'name'
});
new TomSelect('#select', {
valueField: 'id',
labelField: 'name',
searchField: ['name']
});
Tom Select содержит встроенные типы.
import TomSelect fr om 'tom-select';
const control = new TomSelect('#users', {
maxItems: 3
});
control.on('change', (value: string) => {
console.log(value);
});
Старые jQuery-плагины для Selectize обычно несовместимы напрямую.
$.fn.customSelectize = function() {
return this.selectize();
};
function customSelect(selector, options) {
return new TomSelect(selector, options);
}
Tom Sel ect лучше интегрируется с современными фреймворками.
import { useEffect, useRef } fr om 'react';
import TomSelect fr om 'tom-select';
function UserSelect() {
const ref = useRef();
useEffect(() => {
const control = new TomSelect(ref.current);
return () => control.destroy();
}, []);
return <input ref={ref} />;
}
mounted() {
this.control = new TomSelect(this.$refs.select);
},
beforeUnmount() {
this.control.destroy();
}
import TomSelect fr om 'tom-select';
import 'tom-select/dist/css/tom-select.css';
Tom Sel ect корректно работает с современными bundler-системами.
Tom Sel ect поддерживает частичную загрузку модулей.
import TomSelect fr om 'tom-select/base';
Подключение только нужных плагинов:
import TomSelect fr om 'tom-select/base';
import remove_button fr om 'tom-select/plugins/remove_button.js';
TomSelect.define('remove_button', remove_button);
import TomSelect fr om 'tom-select';
import TomSelect fr om 'tom-select/base';
Многие проекты используют промежуточный слой совместимости.
function initSelect(selector, options) {
return new TomSelect(selector, options);
}
Такой подход упрощает постепенную миграцию.
Причина:
Причина:
.trigger();this.Причина:
Причина:
escape();После миграции обычно тестируются:
Tom Select значительно улучшил поддержку accessibility.
Tom Select работает быстрее благодаря:
В крупных проектах миграция обычно выполняется через адаптер.
class LegacySelect {
constructor(selector, options) {
this.instance = new TomSelect(selector, options);
}
clear() {
this.instance.clear();
}
destroy() {
this.instance.destroy();
}
}
Tom Select корректнее работает при динамическом создании компонентов.
control.destroy();
Без уничтожения экземпляров возможны:
$('.dynamic-select').selectize();
document.querySelectorAll('.dynamic-select')
.forEach(element => {
new TomSelect(element);
});
onChange: function(value) {
console.log(value);
}
onChange(value) {
console.log(value);
}
Tom Select ориентирован на современный синтаксис методов объекта.
$('#users').selectize({
valueField: 'id',
labelField: 'name',
searchField: 'name',
create: false,
load: function(query, callback) {
$.ajax({
url: '/api/users',
data: { q: query },
success: callback
});
},
render: {
option: function(item) {
return '<div>' + item.name + '</div>';
}
}
});
new TomSelect('#users', {
valueField: 'id',
labelField: 'name',
searchField: ['name'],
create: false,
load(query, callback) {
fetch(`/api/users?q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => callback(data))
.catch(() => callback());
},
render: {
option(item, escape) {
return `<div>${escape(item.name)}</div>`;
}
}
});