Переход с устаревших решений для
<select>-элементов на Tom Sel ect редко ограничивается
простой заменой инициализации. В большинстве проектов библиотека
интегрирована в существующую архитектуру: используются старые события,
кастомные плагины, специфические CSS-классы, серверные шаблоны,
jQuery-подходы и внутренние API. Обратная совместимость становится
критически важной задачей при постепенной миграции крупных
интерфейсов.
Tom Select создавался как современное развитие Selectize.js, поэтому часть API и поведения сохранена намеренно. Однако между библиотеками существуют различия, которые необходимо учитывать при адаптации старого кода.
<select>Tom Select сохраняет базовую совместимость со стандартными элементами формы. Это означает:
<select>;<option>;<optgroup>;Пример обычного элемента:
<select id="country">
<option value="kz">Казахстан</option>
<option value="ru">Россия</option>
<option value="uz">Узбекистан</option>
</select>
Инициализация:
new TomSelect('#country');
После инициализации исходный <select> не
удаляется. Библиотека скрывает его и синхронизирует состояние с
пользовательским интерфейсом.
Это особенно важно для:
Tom Select не меняет механизм отправки формы. Сервер продолжает
получать значения так же, как и при использовании обычного
<select>.
Пример:
<select id="tags" name="tags[]" multiple>
<option value="js" selected>JavaScript</option>
<option value="css" selected>CSS</option>
</select>
После отправки формы сервер получит:
tags[]=js
tags[]=css
Это позволяет внедрять библиотеку без изменения backend-логики.
Tom Select написан без зависимости от jQuery. Однако во многих старых проектах логика построена именно вокруг него.
Можно создать адаптер:
$.fn.tomselect = function(options) {
return this.each(function() {
if (!this.tomselect) {
new TomSelect(this, options);
}
});
};
Использование:
$('#country').tomselect();
Такой подход помогает сохранить существующий стиль кода.
Во многих legacy-проектах код ожидает определённые методы.
Например:
$('#country')[0].selectize.clear();
Для обратной совместимости можно создать alias:
const control = new TomSelect('#country');
control.selectize = control;
Теперь старый код продолжит работать:
control.selectize.clear();
Tom Select изначально создавался как форк Selectize.js. Большая часть API осталась прежней.
Многие конфигурации работают без изменений:
new TomSelect('#users', {
valueField: 'id',
labelField: 'name',
searchField: 'name',
create: true,
maxItems: 5
});
Большинство методов имеют одинаковые имена:
control.addOption();
control.clear();
control.setValue();
control.getValue();
control.disable();
control.enable();
События также largely сохранены:
control.on('change', value => {
console.log(value);
});
Несмотря на высокий уровень совместимости, существуют различия.
Некоторые HTML-структуры отличаются:
Старые CSS-правила могут перестать работать.
Пример проблемного селектора:
.selectize-dropdown-content {
max-height: 300px;
}
В Tom Select структура может отличаться в зависимости от плагина и темы.
Многие старые темы были написаны под:
Tom Select использует собственные классы:
<div class="ts-wrapper">
Если старый код ожидает:
<div class="selectize-control">
стили могут не применяться.
Для сохранения совместимости можно вручную добавлять старые классы:
const control = new TomSelect('#tags');
control.wrapper.classList.add('selectize-control');
control.dropdown.classList.add('selectize-dropdown');
Это позволяет временно сохранить старую CSS-базу.
Tom Select не зависит от Bootstrap, но может использоваться вместе с ним.
Основные проблемы:
Иногда требуется адаптация:
.ts-wrapper.form-control {
height: auto;
}
Совместимость значительно лучше.
Пример:
new TomSelect('#city', {
create: false
});
<select class="form-select" id="city">
Tom Select не является React-компонентом, но может использоваться внутри React-приложений.
Tom Select напрямую изменяет DOM, тогда как React ожидает полный контроль над ним.
Неправильный подход:
<select value={value}>
Tom Select начнёт конфликтовать с virtual DOM.
Обычно используется ref:
const selectRef = useRef();
useEffect(() => {
const control = new TomSelect(selectRef.current);
return () => control.destroy();
}, []);
Во Vue возникают аналогичные проблемы.
Правильный подход:
mounted() {
this.control = new TomSelect(this.$refs.select);
},
beforeUnmount() {
this.control.destroy();
}
Angular также требует ручного контроля жизненного цикла.
Пример:
ngAfterViewInit() {
this.control = new TomSelect(this.select.nativeElement);
}
ngOnDestroy() {
this.control.destroy();
}
Многие старые проекты используют jQuery AJAX:
$.get('/users', data => {
control.addOptions(data);
});
Tom Select полностью совместим с таким подходом.
Также поддерживается современный fetch.
Tom Select ориентирован на современные браузеры.
В старых браузерах могут отсутствовать:
Promise;fetch;classList;CustomEvent;Array.fr om.Для поддержки legacy-среды требуются polyfill.
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch/dist/fetch.umd.js"></script>
Полноценная поддержка IE отсутствует.
Проблемы:
Для старых корпоративных систем иногда сохраняют Selectize.js вместо миграции.
В крупных проектах часто невозможно сразу заменить все компоненты.
Некоторые формы продолжают использовать старую библиотеку:
if (window.useLegacySelectize) {
$('#users').selectize();
} else {
new TomSelect('#users');
}
Это позволяет выполнять постепенный переход.
В больших системах полезно создать единый слой абстракции.
Пример:
class SelectAdapter {
constructor(selector, options) {
this.instance = new TomSelect(selector, options);
}
clear() {
this.instance.clear();
}
setValue(value) {
this.instance.setValue(value);
}
getValue() {
return this.instance.getValue();
}
}
Теперь приложение зависит не от конкретной библиотеки, а от адаптера.
При миграции важно избегать прямого обращения к внутренним свойствам:
Плохо:
control.dropdown_content.innerHTML = '';
Хорошо:
control.clearOptions();
Внутренние свойства могут измениться между версиями.
Плагины Selectize.js не всегда работают без изменений.
Старый код:
TomSelect.define('legacy_plugin', function() {
this.on('initialize', () => {
console.log('init');
});
});
Может работать без изменений, если не использует внутренние свойства.
Наиболее опасная зона миграции — использование приватных методов:
control.setupTemplates();
или:
control.positionDropdown();
Подобные методы могут изменяться между версиями.
Сначала выделяется весь код работы с select-компонентами.
Создаётся совместимый слой API.
Отдельные страницы переводятся на Tom Select.
После полной миграции убираются:
Некоторые системы используют нативные события:
select.addEventListener('change', handler);
Tom Select синхронизирует состояние с оригинальным
<select>, поэтому такие обработчики продолжают
работать.
Во многих старых системах DOM меняется динамически.
Например:
select.innerHTML = html;
Tom Select не всегда автоматически обнаруживает такие изменения.
Требуется ручной вызов:
control.sync();
При AJAX-подгрузке новых форм важно избегать повторной инициализации.
Проверка:
if (!element.tomselect) {
new TomSelect(element);
}
Без такой проверки возникают:
При серверном рендеринге важно учитывать отсутствие DOM на сервере.
Неправильно:
new TomSelect('#users');
в SSR-контексте.
Правильно:
if (typeof window !== 'undefined') {
new TomSelect('#users');
}
При обновлении Tom Select между версиями необходимо проверять:
Во многих проектах поверх Selectize.js уже существует внутренний компонент:
createUserSelect(selector);
Лучший путь миграции — изменить внутреннюю реализацию:
function createUserSelect(selector) {
return new TomSelect(selector);
}
При этом остальная система не меняется.
Плохо:
if (TomSelect.version === '2.1') {
Хорошо:
if (typeof control.sync === 'function') {
Такой подход устойчивее к обновлениям.
Старые конфиги можно автоматически преобразовывать.
Пример адаптации:
function normalizeConfig(config) {
if (config.max_items) {
config.maxItems = config.max_items;
}
return config;
}
Старые серверы могут ожидать строку:
1,2,3
вместо массива.
Tom Select позволяет настраивать сериализацию:
control.getValue();
или:
control.getValue().join(',');
При миграции особенно важно проверять:
new TomSelect('#users');
new TomSelect('#users');
control.$dropdown
querySelector('.item:last-child')
control.destroy();
value={state}
одновременно с ручным setValue().
Для долгосрочной стабильности рекомендуется: