Синхронизация с оригинальным select

Tom Select работает как надстройка над стандартным HTML <select> и сохраняет с ним двустороннюю синхронизацию. Внутренне библиотека создаёт собственную структуру UI, но исходный элемент остаётся источником состояния формы, что критично для отправки данных, деградации без JavaScript и интеграции с серверным рендерингом.

Ключевая идея заключается в том, что:

  • оригинальный <select> сохраняет актуальное значение;
  • Tom Select поддерживает внутренний массив выбранных элементов;
  • любые изменения должны отражаться в обе стороны;
  • форма всегда опирается на native DOM-состояние.

Инициализация и первичное выравнивание состояния

При создании экземпляра Tom Select происходит чтение текущего состояния <select>:

  • выбранные <option> преобразуются в items;
  • текстовые значения становятся отображаемыми лейблами;
  • атрибут selected интерпретируется как источник истины.

Если <select> уже содержит значения:

<select id="tags" multiple>
  <option value="js" selected>JavaScript</option>
  <option value="ts">TypeScript</option>
</select>

После инициализации:

  • items = ["js"]
  • UI содержит один выбранный тег
  • оригинальный <select> остаётся неизменённым, кроме внутренней синхронизации состояния объекта

Внутреннее представление состояния

Tom Select разделяет состояние на три уровня:

  1. DOM <select>

    • <option selected>
    • value
    • участие в submit формы
  2. Внутренний state Tom Select

    • массив items
    • объектные данные опций
    • кеш поиска
  3. UI слой

    • теги (для multiple)
    • dropdown список
    • input поле

Синхронизация всегда проходит через state слой, который затем транслируется в DOM.


Изменение значения через API и отражение в <select>

setValue как основной механизм синхронизации

Метод setValue() является базовой точкой синхронизации:

  • полностью заменяет текущее значение;
  • перезаписывает selected у <option>;
  • обновляет UI;
  • генерирует события изменения.

Поведение:

  • переданное значение нормализуется;
  • выполняется поиск соответствующих опций;
  • старые значения снимаются;
  • новые отмечаются как selected.

addItem и removeItem как инкрементальная синхронизация

addItem(value):

  • добавляет значение в state;
  • помечает <option selected>;
  • не затрагивает остальные элементы.

removeItem(value):

  • снимает выбор с конкретного option;
  • обновляет массив items;
  • синхронизирует UI без полной перерисовки.

Эти методы обеспечивают более стабильную синхронизацию при частичных изменениях.


Обратная синхронизация: изменения в <select> и DOM

Изменения, происходящие напрямую в <select>, могут быть подхвачены через:

  • события change;
  • ручной вызов refreshItems() (внутренние механизмы);
  • повторную синхронизацию состояния.

Пример внешнего изменения:

select.value = "ts";
select.dispatchEvent(new Event("change"));

В этом случае Tom Select:

  • считывает новое значение;
  • обновляет items;
  • синхронизирует UI.

Обработка события change

Событие change является ключевым каналом обратной связи.

При изменениях:

  • пользователь выбирает элемент в UI;
  • вызывается обновление внутреннего state;
  • пересчитывается <select>.value;
  • генерируется native change.

Важно, что Tom Select старается сохранить совместимость с обычной формой:

  • form.submit() получает актуальные значения;
  • сервер не знает о наличии JS-слоя.

Синхронизация при multiple select

Для multiple-режима используется массив значений:

<select multiple>

Особенности:

  • каждый выбранный элемент соответствует отдельному <option selected>;
  • value <select> становится агрегированным;
  • порядок items влияет на порядок selected options.

При удалении элемента:

  • снимается selected только с одного option;
  • остальные остаются нетронутыми.

Динамическое изменение options

Добавление новых опций требует явной синхронизации:

  • через addOption();
  • через прямое изменение DOM;
  • через перезагрузку списка.

После добавления:

  • необходимо обновление внутреннего cache;
  • вызов refreshOptions();
  • синхронизация с текущим значением.

Пример логики:

select.addOption({value: "go", text: "Go"});
select.refreshOptions(false);

Если этого не сделать:

  • UI может не отображать новые значения;
  • внутренний state и DOM расходятся.

Удаление и скрытие опций

Удаление <option> из DOM требует аккуратности:

  • если значение уже выбрано, оно должно быть удалено из items;
  • иначе появится «висящее» значение.

Типичный сценарий:

  • удаляется option;
  • вызывается пересинхронизация;
  • items фильтруются по существующим option.value.

Синхронизация при программной очистке

Очистка состояния:

  • clear() снимает все выбранные значения;
  • удаляет selected у всех <option>;
  • обновляет UI до пустого состояния.

Для multiple:

  • очищается весь массив items;
  • DOM полностью сбрасывается.

Silent-обновления без событий

В ряде сценариев требуется обновление без триггера событий:

  • загрузка состояния из сервера;
  • массовая инициализация;
  • восстановление формы.

Используется режим подавления событий:

  • изменения state;
  • обновление DOM;
  • без генерации change.

Это предотвращает каскадные обработчики.


Синхронизация при destroy и восстановлении native select

Метод destroy() выполняет обратную операцию:

  • удаляет UI-обёртку;
  • возвращает видимость оригинального <select>;
  • оставляет актуальные selected значения;
  • восстанавливает стандартное поведение формы.

Состояние не теряется, так как:

  • <option selected> остаются в DOM;
  • значение сохраняется в native элементе.

Работа с optgroup и групповой синхронизацией

При использовании <optgroup>:

  • структура групп сохраняется;
  • selected применяется внутри групп;
  • UI отражает вложенность.

Особенности синхронизации:

  • изменение option внутри группы требует refresh;
  • перемещение option между группами требует пересчёта кеша;
  • value остаётся привязанным к option.value, а не к позиции.

Конфликты состояния и их разрешение

Расхождение может возникать при:

  • ручном изменении DOM;
  • асинхронной загрузке опций;
  • частичном обновлении формы.

Типичный механизм восстановления:

  • перечитывание <select>;
  • пересборка items;
  • перерисовка UI.

Приоритет всегда остаётся за DOM <select> как источником истины.


Итоговая модель поведения синхронизации

Система синхронизации Tom Select опирается на постоянное согласование трёх слоёв:

  • native <select> как источник формы;
  • internal state как логический слой;
  • UI как визуальное представление.

Любое изменение проходит через один из каналов API или DOM-событий и приводит к приведению всех слоёв к согласованному состоянию без разрыва формы и стандартного поведения браузера.