Структура HTML разметки

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

После инициализации исходный элемент оборачивается в основной контейнер:

<div class="ts-wrapper">
  ...
</div>

Этот контейнер становится корневым элементом, через который реализуются:

  • позиционирование dropdown
  • управление фокусом
  • обработка состояний (disabled, focus, open)
  • визуальная синхронизация с оригинальным <select>

Исходный <select> или <input> либо скрывается, либо заменяется логической моделью, но сохраняется в DOM для синхронизации значений.


Скрытый input и синхронизация состояния

При использовании <select> создаётся скрытая копия значения:

<select class="original-select" hidden>
  <option value="1">Option 1</option>
</select>

или:

<input type="text" class="ts-control-input" autocomplete="off">

Скрытый input выполняет функции:

  • хранение выбранных значений
  • синхронизация с form submit
  • интеграция с нативной валидацией формы

Основной control-узел

Центральный элемент интерфейса:

<div class="ts-control">
  <div class="ts-input">
    <input type="text" autocomplete="off">
  </div>
</div>

Назначение узлов:

  • ts-control — область взаимодействия (клик, фокус, ввод)
  • ts-input — контейнер для текстового ввода
  • input — поле поиска и фильтрации

При мультивыборе внутри ts-control дополнительно добавляются токены:

<div class="ts-item">Value 1 <span class="remove">×</span></div>

Представление выбранных значений

В режиме single select выбранный элемент может отображаться как текст:

<div class="ts-control">
  <div class="ts-single">Option 1</div>
</div>

В режиме multi select используется набор элементов:

<div class="ts-control">
  <div class="ts-item">Item 1 <span class="remove">×</span></div>
  <div class="ts-item">Item 2 <span class="remove">×</span></div>
  <input type="text">
</div>

Структурные особенности:

  • каждый выбранный элемент — отдельный DOM-узел
  • удаление реализуется через кнопку внутри элемента
  • порядок элементов соответствует внутреннему массиву состояния

Выпадающий список создаётся отдельно от control:

<div class="ts-dropdown">
  <div class="ts-dropdown-content">
    ...
  </div>
</div>

Основные элементы:

  • ts-dropdown — позиционируемый контейнер
  • ts-dropdown-content — список опций
  • ts-option — отдельная опция

Структура опций

Каждая опция в списке имеет стандартную структуру:

<div class="ts-option" data-value="1">
  Option 1
</div>

Состояния опций:

<div class="ts-option active"></div>
<div class="ts-option selected"></div>
<div class="ts-option disabled"></div>

Значения классов:

  • active — текущая навигация клавиатурой
  • selected — уже выбранное значение
  • disabled — недоступная опция

Поддержка optgroup

Группировка опций реализуется через контейнеры:

<div class="ts-optgroup">
  <div class="ts-optgroup-label">Group 1</div>
  <div class="ts-option">A</div>
  <div class="ts-option">B</div>
</div>

Структурные элементы:

  • ts-optgroup — контейнер группы
  • ts-optgroup-label — заголовок группы
  • вложенные ts-option — элементы группы

Группы могут быть вложены только логически, но не рекурсивно.


Поисковая строка и фильтрация

Input для поиска находится внутри control:

<input type="text" class="ts-input" autocomplete="off">

Функционально он:

  • фильтрует ts-option
  • управляет открытием dropdown
  • обновляет состояние active элемента

При вводе текста изменяется видимость опций через:

ts-option[hidden]

или через динамическое удаление из DOM (в зависимости от конфигурации).


Состояния контейнера

Корневой элемент изменяет классы в зависимости от состояния:

<div class="ts-wrapper focus"></div>
<div class="ts-wrapper dropdown-active"></div>
<div class="ts-wrapper disabled"></div>

Основные состояния:

  • focus — активный фокус ввода
  • dropdown-active — открыт список
  • disabled — блокировка взаимодействия
  • loading — ожидание данных

Data-атрибуты и внутренняя идентификация

Каждая опция может содержать служебные атрибуты:

<div class="ts-option" data-value="10" data-selectable="1"></div>

Используемые атрибуты:

  • data-value — значение элемента
  • data-selectable — доступность выбора
  • data-disabled — блокировка

Внутренняя модель синхронизирует эти значения с состоянием компонента.


Управление caret и вставка элементов

В multi-select режиме caret (курсор вставки) представлен как часть DOM:

<div class="ts-control">
  <div class="ts-item">A</div>
  <input class="ts-input">
</div>

Caret позиция влияет на:

  • вставку нового значения
  • порядок элементов
  • поведение backspace удаления

Рендеринг кастомных шаблонов

HTML-структура опций может быть расширена через шаблоны:

<div class="ts-option">
  <span class="title">Option</span>
  <span class="subtitle">Description</span>
</div>

При этом базовый класс ts-option остаётся обязательным, так как он участвует в:

  • обработке кликов
  • навигации клавиатурой
  • выборе значения

Accessibility-структура

Tom Select формирует ARIA-совместимую структуру:

<div role="combobox" aria-expanded="false">
  <input aria-autocomplete="list">
  <div role="listbox">
    <div role="option"></div>
  </div>
</div>

Основные роли:

  • combobox — основной контрол
  • listbox — контейнер списка
  • option — элементы выбора

ARIA-атрибуты обновляются синхронно с DOM:

  • aria-expanded
  • aria-selected
  • aria-activedescendant

Динамическая перестройка DOM

DOM-структура не статична и может изменяться:

  • добавление новых ts-option при async загрузке
  • удаление элементов при фильтрации
  • перерасчёт порядка при drag-and-drop
  • пересборка dropdown при изменении конфигурации

Каждое изменение отражается в следующих узлах:

  • ts-wrapper
  • ts-control
  • ts-dropdown-content

Внутренние служебные элементы

Дополнительные элементы, создаваемые библиотекой:

<div class="ts-loading">Loading...</div>
<div class="ts-no-results">No results found</div>

Они появляются внутри ts-dropdown-content и управляются состоянием поиска и загрузки данных.