Создание групп

Группы (optgroup) в Tom Select используются для логического разделения набора опций на категории. Такой подход особенно полезен при работе с большими списками данных:

  • каталоги товаров;
  • списки стран и городов;
  • категории тегов;
  • роли пользователей;
  • классификаторы;
  • иерархические структуры.

Tom Select поддерживает как работу с обычными HTML-группами <optgroup>, так и динамическое создание групп через JavaScript API.


HTML-структура групп

Базовый способ создания групп основан на стандартном HTML-элементе <optgroup>.

<select id="frameworks" multiple>
    <optgroup label="Frontend">
        <option value="vue">Vue</option>
        <option value="react">React</option>
        <option value="svelte">Svelte</option>
    </optgroup>

    <optgroup label="Backend">
        <option value="node">Node.js</option>
        <option value="laravel">Laravel</option>
        <option value="django">Django</option>
    </optgroup>
</select>

Инициализация:

new TomSelect("#frameworks");

После инициализации Tom Select автоматически распознаёт группы и отображает их как отдельные секции выпадающего списка.


Визуальная структура optgroup

Tom Select преобразует HTML-группы в собственную внутреннюю структуру:

<div class="optgroup">
    <div class="optgroup-header">Frontend</div>

    <div data-value="vue" class="option">
        Vue
    </div>

    <div data-value="react" class="option">
        React
    </div>
</div>

Это позволяет:

  • стилизовать заголовки;
  • скрывать группы;
  • управлять порядком;
  • добавлять кастомную разметку;
  • динамически обновлять категории.

Создание групп через JavaScript

Tom Select поддерживает декларативное создание групп через параметры:

  • options
  • optgroups
  • optgroupField

Полная конфигурация групп

new TomSelect("#select", {
    optgroupField: "category",

    optgroups: [
        {
            value: "frontend",
            label: "Frontend"
        },
        {
            value: "backend",
            label: "Backend"
        }
    ],

    options: [
        {
            value: "vue",
            text: "Vue",
            category: "frontend"
        },
        {
            value: "react",
            text: "React",
            category: "frontend"
        },
        {
            value: "node",
            text: "Node.js",
            category: "backend"
        }
    ]
});

Параметр optgroupField

Назначение

optgroupField определяет поле объекта, которое содержит идентификатор группы.

optgroupField: "category"

Внутри опции:

{
    value: "react",
    text: "React",
    category: "frontend"
}

Tom Select определяет:

  • опция принадлежит группе frontend;
  • группа ищется внутри массива optgroups.

Параметр optgroups

Структура группы

Каждая группа представляет собой объект:

{
    value: "frontend",
    label: "Frontend"
}

Поля группы

Поле Назначение
value внутренний идентификатор
label отображаемое название

Несколько групп

optgroups: [
    {
        value: "frontend",
        label: "Frontend"
    },
    {
        value: "backend",
        label: "Backend"
    },
    {
        value: "database",
        label: "Databases"
    }
]

Привязка элементов к группам

Связь выполняется через поле, указанное в optgroupField.

options: [
    {
        value: "mysql",
        text: "MySQL",
        category: "database"
    }
]

Использование нескольких групп у одной опции

Tom Select поддерживает массив групп.

Настройка

new TomSelect("#select", {
    optgroupField: "groups",
    optgroupValueField: "id",
    optgroupLabelField: "name",

    optgroups: [
        {
            id: "js",
            name: "JavaScript"
        },
        {
            id: "backend",
            name: "Backend"
        }
    ],

    options: [
        {
            value: "node",
            text: "Node.js",
            groups: ["js", "backend"]
        }
    ]
});

Настройка названий полей групп

По умолчанию Tom Select ожидает:

{
    value: "...",
    label: "..."
}

Но структура может быть изменена.


optgroupValueField

Указывает поле идентификатора.

optgroupValueField: "id"

optgroupLabelField

Определяет поле отображаемого имени.

optgroupLabelField: "title"

Полный пример

new TomSelect("#select", {
    optgroupField: "group",

    optgroupValueField: "id",
    optgroupLabelField: "title",

    optgroups: [
        {
            id: "frontend",
            title: "Frontend"
        }
    ],

    options: [
        {
            value: "vue",
            text: "Vue",
            group: "frontend"
        }
    ]
});

Динамическое создание групп

addOptionGroup()

Tom Select позволяет создавать группы после инициализации.

const control = new TomSelect("#select");

control.addOptionGroup("backend", {
    label: "Backend"
});

Добавление опций в группу

control.addOption({
    value: "nestjs",
    text: "NestJS",
    optgroup: "backend"
});

Удаление групп

removeOptionGroup()

control.removeOptionGroup("backend");

После удаления:

  • группа исчезает;
  • связанные опции больше не отображаются внутри неё.

Обновление групп

Группы можно перезаписывать повторным вызовом:

control.addOptionGroup("frontend", {
    label: "Frontend Frameworks"
});

Очистка групп

clearOptionGroups()

control.clearOptionGroups();

Удаляются:

  • все группы;
  • внутренняя карта категорий.

Опции при этом могут остаться.


Сортировка групп

Tom Select поддерживает сортировку групп через lockOptgroupOrder.


lockOptgroupOrder

new TomSelect("#select", {
    lockOptgroupOrder: true
});

Поведение

Если параметр включён:

  • группы отображаются в порядке объявления;
  • сортировка результатов поиска не меняет порядок категорий.

Без lockOptgroupOrder

Tom Select может менять порядок групп динамически:

  • по результатам поиска;
  • по совпадениям;
  • по релевантности.

Скрытие пустых групп

Tom Select автоматически скрывает группы без элементов.

Например:

{
    value: "empty",
    label: "Empty Group"
}

Если в группе нет опций — заголовок не отображается.


Кастомный рендер групп

Через render.optgroup_header можно полностью изменить отображение заголовков.


Пример кастомного заголовка

new TomSelect("#select", {
    render: {
        optgroup_header(data, escape) {
            return `
                <div class="group-header">
                    Категория: ${escape(data.label)}
                </div>
            `;
        }
    }
});

Добавление счётчика элементов

new TomSelect("#select", {
    render: {
        optgroup_header(data) {
            return `
                <div class="group-header">
                    ${data.label}
                    <span class="count">
                        (${data.$order})
                    </span>
                </div>
            `;
        }
    }
});

Стилизация групп

Стили заголовка

.ts-dropdown .optgroup-header {
    font-weight: bold;
    padding: 10px;
    background: #f3f3f3;
}

Отступы элементов группы

.ts-dropdown .optgroup .option {
    padding-left: 20px;
}

Вложенные группы

Tom Select не поддерживает настоящие вложенные группы:

<optgroup>
    <optgroup>

Подобная структура недоступна в HTML-спецификации.


Эмуляция вложенности

Вложенность обычно реализуют:

  • префиксами;
  • дополнительными отступами;
  • кастомным рендером.

Пример псевдовложенности

options: [
    {
        value: "vue2",
        text: "— Vue 2",
        group: "frontend"
    },
    {
        value: "vue3",
        text: "— Vue 3",
        group: "frontend"
    }
]

Группы и поиск

Поиск Tom Select работает:

  • внутри всех групп;
  • одновременно по всем элементам.

Группы не ограничивают область поиска.


Пример

При вводе:

vue

Tom Select:

  • ищет совпадения глобально;
  • показывает только группы с найденными элементами.

Группы и remote data

Группы особенно полезны при загрузке данных с сервера.


Пример API-структуры

[
    {
        "value": "vue",
        "text": "Vue",
        "category": "frontend"
    },
    {
        "value": "laravel",
        "text": "Laravel",
        "category": "backend"
    }
]

Инициализация

new TomSelect("#select", {
    valueField: "value",
    labelField: "text",

    optgroupField: "category",

    optgroups: [
        {
            value: "frontend",
            label: "Frontend"
        },
        {
            value: "backend",
            label: "Backend"
        }
    ],

    load(query, callback) {
        fetch(`/api/search?q=${query}`)
            .then(response => response.json())
            .then(data => callback(data));
    }
});

Автоматическое создание групп из API

Группы можно генерировать динамически.


Пример

fetch("/api/categories")
    .then(response => response.json())
    .then(groups => {

        groups.forEach(group => {
            control.addOptionGroup(group.id, {
                label: group.name
            });
        });

    });

Группы и selected items

После выбора элемента:

  • информация о группе сохраняется;
  • но выбранный элемент отображается отдельно от группы.

Группировка работает только внутри dropdown.


Группы и multiple

Группы особенно полезны в режиме множественного выбора:

new TomSelect("#select", {
    maxItems: null
});

При большом количестве элементов категории значительно улучшают навигацию.


Использование disabled-групп

Tom Select позволяет отключать группы.


Пример

optgroups: [
    {
        value: "premium",
        label: "Premium",
        disabled: true
    }
]

Поведение disabled

Отключённая группа:

  • визуально помечается;
  • не позволяет выбирать элементы;
  • блокирует взаимодействие с опциями.

Кастомное оформление disabled-групп

.ts-dropdown .optgroup.disabled .optgroup-header {
    opacity: 0.5;
}

Производительность при большом количестве групп

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

  • использовать remote loading;
  • включать виртуализацию;
  • ограничивать число результатов;
  • избегать тяжёлого HTML внутри render.optgroup_header.

Типичные ошибки

Несовпадение optgroupField

Ошибка:

optgroupField: "category"

Но в option:

{
    group: "frontend"
}

Группы не будут работать.


Отсутствие группы

{
    value: "vue",
    text: "Vue",
    category: "frontend"
}

Если группа frontend не зарегистрирована в optgroups, элемент может отображаться некорректно.


Дублирование идентификаторов групп

Ошибка:

{
    value: "frontend"
}

повторяется несколько раз.

Идентификаторы групп должны быть уникальными.


Практический пример каталога

new TomSelect("#products", {
    optgroupField: "category",

    optgroups: [
        {
            value: "phones",
            label: "Смартфоны"
        },
        {
            value: "laptops",
            label: "Ноутбуки"
        },
        {
            value: "tablets",
            label: "Планшеты"
        }
    ],

    options: [
        {
            value: "iphone",
            text: "iPhone 15",
            category: "phones"
        },
        {
            value: "macbook",
            text: "MacBook Pro",
            category: "laptops"
        },
        {
            value: "ipad",
            text: "iPad Air",
            category: "tablets"
        }
    ]
});

Практический пример с динамическими группами

const control = new TomSelect("#skills", {
    optgroupField: "type"
});

const groups = [
    ["frontend", "Frontend"],
    ["backend", "Backend"],
    ["devops", "DevOps"]
];

groups.forEach(group => {

    control.addOptionGroup(group[0], {
        label: group[1]
    });

});

control.addOption({
    value: "docker",
    text: "Docker",
    type: "devops"
});

control.refreshOptions(false);

Внутреннее хранение групп

Tom Select хранит группы внутри:

control.optgroups

Пример

console.log(control.optgroups);

Результат:

{
    frontend: {
        label: "Frontend"
    },

    backend: {
        label: "Backend"
    }
}

Проверка существования группы

if (control.optgroups.backend) {
    console.log("Группа существует");
}

Изменение группы напрямую

control.optgroups.backend.label = "Server Side";

После изменения:

control.refreshOptions(false);

Полное обновление dropdown

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

control.clearCache();
control.refreshOptions(false);

Это заставляет Tom Select:

  • пересоздать HTML;
  • обновить заголовки;
  • пересчитать группы;
  • заново отрисовать dropdown.