Группировка в Tom Select строится вокруг сущности
optgroup, которая задаёт логическое объединение
элементов списка. Каждый элемент может принадлежать одной группе через
поле optgroup, а сами группы описываются отдельно через
optgroups.
Базовая структура данных:
const select = new TomSelect('#select', {
options: [
{ value: 'js', text: 'JavaScript', optgroup: 'frontend' },
{ value: 'ts', text: 'TypeScript', optgroup: 'frontend' },
{ value: 'node', text: 'Node.js', optgroup: 'backend' },
{ value: 'go', text: 'Go', optgroup: 'backend' }
],
optgroups: [
{ value: 'frontend', label: 'Frontend' },
{ value: 'backend', label: 'Backend' }
]
});
В такой модели существует два независимых уровня порядка:
Оба уровня могут управляться отдельно и комбинироваться через настройки сортировки.
Основной механизм сортировки элементов задаётся через
sortField. Это свойство определяет, по какому полю и в
каком порядке сравниваются элементы внутри одной группы.
Простейший вариант:
const select = new TomSelect('#select', {
sortField: 'text'
});
В этом случае элементы внутри каждой группы сортируются по текстовому
значению text.
Поддерживается также направление сортировки:
sortField: {
field: 'text',
direction: 'asc'
}
или обратный порядок:
sortField: {
field: 'text',
direction: 'desc'
}
При одинаковых значениях ключа сортировки порядок может зависеть от
исходного массива options. Это важно при динамической
загрузке данных, когда сервер не гарантирует стабильный порядок.
sortField поддерживает массив правил, что позволяет
задавать приоритеты сортировки.
sortField: [
{ field: 'priority', direction: 'desc' },
{ field: 'text', direction: 'asc' }
]
Такой подход формирует каскадное сравнение:
prioritytextЭто критично для групп, где элементы имеют разные уровни важности, но должны оставаться упорядоченными внутри одной логической категории.
При использовании групп важно учитывать, что сортировка может быть привязана не только к полям элементов, но и к их принадлежности к группе.
Расширенная форма sortField:
sortField: [
{ field: 'optgroup', direction: 'asc' },
{ field: 'text', direction: 'asc' }
]
В этом случае происходит предварительное упорядочивание по имени группы, а затем внутри неё — по тексту.
Однако в большинстве случаев более корректно разделять:
Порядок групп не всегда зависит от сортировки элементов. Он
определяется отдельно через массив optgroups.
optgroups: [
{ value: 'backend', label: 'Backend' },
{ value: 'frontend', label: 'Frontend' }
]
Здесь группы выводятся строго в заданном порядке.
Если требуется сортировка групп по полю, применяется кастомная логика:
optgroups: [
{ value: 'frontend', label: 'Frontend', order: 2 },
{ value: 'backend', label: 'Backend', order: 1 }
]
И затем:
optgroupOrder: 'order'
Когда требуется динамическая сортировка, используется функция сравнения.
optgroups: [
{ value: 'frontend', label: 'Frontend', weight: 20 },
{ value: 'backend', label: 'Backend', weight: 10 }
],
optgroupOrder: (a, b) => a.weight - b.weight
Функция сравнения получает два объекта группы и должна возвращать:
Комбинированная модель поведения выглядит следующим образом:
Пример комбинированной конфигурации:
new TomSelect('#select', {
optgroups: [
{ value: 'backend', label: 'Backend', weight: 1 },
{ value: 'frontend', label: 'Frontend', weight: 2 }
],
optgroupOrder: (a, b) => a.weight - b.weight,
sortField: [
{ field: 'priority', direction: 'desc' },
{ field: 'text', direction: 'asc' }
]
});
Сортировка внутри групп сохраняется даже после применения поиска, однако изменяется набор элементов, попадающих в каждую группу.
Важно учитывать:
Если используется кастомный score, он влияет на
релевантность, но не заменяет sortField.
При использовании load данные могут поступать частями. В
таком случае сортировка выполняется после добавления новых
элементов.
load: function(query, callback) {
fetch('/api/items?q=' + query)
.then(res => res.json())
.then(data => callback(data));
}
После вызова callback Tom Select:
sortFieldЕсли порядок важен на уровне сервера, можно частично разгрузить клиент, передавая уже отсортированные данные.
Для сложных сценариев используется функция сравнения вместо
декларативного sortField.
sortField: (a, b) => {
if (a.priority !== b.priority) {
return b.priority - a.priority;
}
return a.text.localeCompare(b.text);
}
Такой подход позволяет учитывать:
При наличии визуальных разделителей или кастомного
render.optgroup_header порядок групп остаётся логическим, а
не визуальным.
render: {
optgroup_header: (data) => {
return `<div class="group-header">${data.label}</div>`;
}
}
Сортировка групп при этом не зависит от рендера и определяется
исключительно optgroupOrder.
При изменении options или optgroups через
API:
Пример обновления:
select.addOption({ value: 'rust', text: 'Rust', optgroup: 'backend' });
select.refreshOptions(false);
После этого все правила сортировки применяются повторно ко всей структуре.
Распространённая модель для сложных интерфейсов:
optgroupOrder: (a, b) => a.rank - b.rank,
sortField: [
{ field: 'usage', direction: 'desc' },
{ field: 'text', direction: 'asc' }
]
Такой подход обеспечивает предсказуемую структуру даже при большом количестве данных и динамическом обновлении списка.