Настройка создания элементов

Механизм создания элементов в библиотеке Tom Select позволяет пользователю вводить значения, отсутствующие в списке опций, после чего эти значения автоматически превращаются в новые selectable-элементы. Такая возможность особенно полезна при работе с тегами, категориями, метками, адресами электронной почты, навыками, пользовательскими фильтрами и любыми динамическими наборами данных.

Создание элементов регулируется несколькими параметрами:

  • create
  • createOnBlur
  • createFilter
  • persist
  • delimiter
  • пользовательскими функциями генерации объектов

Базовый пример:

<select id="tags" multiple>
    <option value="javascript">JavaScript</option>
    <option value="php">PHP</option>
</select>
new TomSelect('#tags', {
    create: true
});

После включения create: true пользователь получает возможность вводить новые значения вручную. При нажатии Enter библиотека создаёт новый элемент и сразу добавляет его в список выбранных.


Поведение параметра create

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

Наиболее распространённый вариант:

new TomSelect('#tags', {
    create: true
});

В этом режиме:

  • разрешается создание новых значений;
  • введённый текст становится value;
  • текст также используется как отображаемый label;
  • объект создаётся автоматически.

Например, ввод:

TypeScript

создаёт:

{
    value: 'TypeScript',
    text: 'TypeScript'
}

Использование функции create

Параметр create может принимать функцию, позволяющую полностью контролировать структуру создаваемого объекта.

new TomSelect('#tags', {
    create: function(input) {
        return {
            value: input.toLowerCase(),
            text: input,
            custom: true
        };
    }
});

Теперь ввод:

React

создаёт:

{
    value: 'react',
    text: 'React',
    custom: true
}

Такой подход применяется для:

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

Создание элементов с callback

Tom Select поддерживает асинхронное создание через callback.

new TomSelect('#users', {
    create: function(input, callback) {

        const user = {
            value: Date.now(),
            text: input
        };

        callback(user);
    }
});

Функция получает:

Аргумент Назначение
input введённое значение
callback функция завершения создания

Асинхронное создание через сервер

Нередко новые элементы должны сначала сохраняться в базе данных.

new TomSelect('#categories', {
    create: function(input, callback) {

        fetch('/api/categories', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                name: input
            })
        })
        .then(response => response.json())
        .then(data => {

            callback({
                value: data.id,
                text: data.name
            });

        })
        .catch(() => {
            callback();
        });
    }
});

Важные особенности:

  • до вызова callback() элемент не добавляется;
  • callback() без аргументов отменяет создание;
  • можно выполнять серверную валидацию;
  • можно предотвращать дубликаты на уровне БД.

createOnBlur

Параметр createOnBlur автоматически создаёт элемент при потере фокуса.

new TomSelect('#tags', {
    create: true,
    createOnBlur: true
});

Поведение:

  1. пользователь вводит текст;
  2. поле теряет фокус;
  3. значение автоматически создаётся.

Особенно полезно для:

  • email-полей;
  • тегов;
  • быстрого ввода;
  • мобильных интерфейсов.

createFilter

Ограничение создания элементов

Параметр createFilter позволяет контролировать допустимые значения.

new TomSelect('#emails', {
    create: true,

    createFilter: function(input) {
        return input.includes('@');
    }
});

Теперь создание возможно только для строк с символом @.


Проверка через регулярное выражение

new TomSelect('#emails', {
    create: true,

    createFilter: function(input) {

        return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input);

    }
});

Подобная проверка полезна для:

  • email;
  • URL;
  • телефонных номеров;
  • slug;
  • кодов;
  • UUID.

Блокировка дубликатов

Tom Select умеет автоматически предотвращать повторяющиеся элементы, однако дополнительная проверка часто используется вручную.

new TomSelect('#skills', {

    create: true,

    createFilter: function(input) {

        const exists = this.options.hasOwnProperty(
            input.toLowerCase()
        );

        return !exists;
    }
});

persist

Сохранение пользовательских элементов

Параметр persist определяет, остаются ли созданные элементы в списке после удаления.

new TomSelect('#tags', {
    create: true,
    persist: false
});

Поведение:

persist Результат
true элемент сохраняется
false элемент удаляется после deselect

Разница между persist: true и persist: false

persist: true

new TomSelect('#tags', {
    create: true,
    persist: true
});

Сценарий:

  1. создаётся тег;
  2. тег выбирается;
  3. тег удаляется из selected;
  4. тег остаётся доступным в dropdown.

persist: false

new TomSelect('#tags', {
    create: true,
    persist: false
});

Сценарий:

  1. создаётся тег;
  2. тег выбирается;
  3. тег удаляется;
  4. объект полностью исчезает.

Генерация slug при создании

Практический сценарий:

new TomSelect('#categories', {

    create: function(input) {

        const slug = input
            .toLowerCase()
            .replace(/\s+/g, '-');

        return {
            value: slug,
            text: input
        };
    }
});

Ввод:

Web Development

превращается в:

{
    value: 'web-development',
    text: 'Web Development'
}

Создание объектов со сложной структурой

Tom Select поддерживает дополнительные поля.

new TomSelect('#products', {

    valueField: 'id',
    labelField: 'title',
    searchField: 'title',

    create: function(input) {

        return {
            id: crypto.randomUUID(),
            title: input,
            type: 'custom',
            createdAt: Date.now()
        };
    }
});

Использование пользовательских render-функций

Созданные элементы можно визуально выделять.

new TomSelect('#tags', {

    create: function(input) {

        return {
            value: input,
            text: input,
            custom: true
        };
    },

    render: {

        option: function(data, escape) {

            if (data.custom) {

                return `
                    <div class="custom-option">
                        ${escape(data.text)}
                        <span>new</span>
                    </div>
                `;
            }

            return `
                <div>
                    ${escape(data.text)}
                </div>
            `;
        }
    }
});

Настройка текста создания

Tom Select показывает специальную строку:

Add "React"

Её можно изменить через render.option_create.

new TomSelect('#tags', {

    create: true,

    render: {

        option_create: function(data, escape) {

            return `
                <div class="create">
                    Создать тег:
                    <strong>${escape(data.input)}</strong>
                </div>
            `;
        }
    }
});

Управление порядком отображения

Созданный элемент может отображаться:

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

Пример сортировки:

new TomSelect('#tags', {

    create: true,

    sortField: [
        {
            field: '$score'
        },
        {
            field: '$order'
        }
    ]
});

Создание нескольких элементов через delimiter

Tom Select умеет автоматически разбивать ввод.

new TomSelect('#tags', {

    create: true,
    delimiter: ','
});

Ввод:

HTML,CSS,JavaScript

создаёт три отдельных элемента.


Работа с paste

При вставке текста Tom Select может автоматически создавать элементы.

new TomSelect('#skills', {
    create: true,
    delimiter: ','
});

Вставка:

Vue,React,Svelte

создаёт:

  • Vue
  • React
  • Svelte

Предварительная обработка input

Часто требуется очистка значения.

new TomSelect('#tags', {

    create: function(input) {

        input = input.trim();

        if (!input.length) {
            return false;
        }

        return {
            value: input.toLowerCase(),
            text: input
        };
    }
});

Отмена создания элемента

Создание можно прервать.

new TomSelect('#tags', {

    create: function(input) {

        if (input.length < 3) {
            return false;
        }

        return {
            value: input,
            text: input
        };
    }
});

Ограничение по длине

new TomSelect('#tags', {

    create: true,

    createFilter: function(input) {
        return input.length <= 20;
    }
});

Создание только буквенных значений

new TomSelect('#codes', {

    create: true,

    createFilter: function(input) {

        return /^[a-zа-яё]+$/i.test(input);

    }
});

При удалённой загрузке данных создание элементов особенно востребовано.

new TomSelect('#users', {

    valueField: 'id',
    labelField: 'name',
    searchField: 'name',

    load: function(query, callback) {

        fetch('/api/users?q=' + query)
            .then(response => response.json())
            .then(data => callback(data))
            .catch(() => callback());
    },

    create: true
});

Если сервер не находит значение, пользователь может создать собственное.


Использование create вместе с maxItems

new TomSelect('#tags', {

    create: true,
    maxItems: 5
});

После достижения лимита создание блокируется автоматически.


Автоматическое добавление после создания

Tom Select сразу выбирает созданный элемент. Это стандартное поведение:

new TomSelect('#tags', {
    create: true
});

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

  • элемент попадает в options;
  • элемент становится selected;
  • input очищается.

Создание элементов в single select

<select id="country">
    <option value="kz">Казахстан</option>
    <option value="ru">Россия</option>
</select>
new TomSelect('#country', {
    create: true
});

Теперь пользователь может вводить собственные страны.


Создание элементов в input-режиме

<input id="skills">
new TomSelect('#skills', {

    create: true,
    delimiter: ','
});

Такой режим часто используется для:

  • тегов;
  • email;
  • ключевых слов;
  • категорий.

Внутренний жизненный цикл создания

При создании нового элемента Tom Select выполняет последовательность:

  1. ввод текста;
  2. фильтрация через createFilter;
  3. вызов create;
  4. добавление объекта через addOption;
  5. выбор через addItem;
  6. обновление dropdown;
  7. синхронизация с оригинальным input/select.

Понимание этого цикла важно при интеграции:

  • с сервером;
  • с state manager;
  • с Vue;
  • с React;
  • с backend API.

События, связанные с созданием

onItemAdd

new TomSelect('#tags', {

    create: true,

    onItemAdd: function(value) {
        console.log('Добавлен:', value);
    }
});

onOptionAdd

new TomSelect('#tags', {

    create: true,

    onOptionAdd: function(value, data) {
        console.log(data);
    }
});

Событие вызывается именно при добавлении новой option.


Создание элементов с HTML-разметкой

new TomSelect('#users', {

    create: function(input) {

        return {
            value: input,
            text: input,
            avatar: '/img/default.png'
        };
    },

    render: {

        option: function(item, escape) {

            return `
                <div class="user">
                    <img src="${item.avatar}">
                    <span>${escape(item.text)}</span>
                </div>
            `;
        }
    }
});

Проблемы безопасности

При пользовательском вводе необходимо экранировать HTML.

Небезопасный вариант:

return `<div>${data.text}</div>`;

Безопасный вариант:

return `<div>${escape(data.text)}</div>`;

Функция escape() защищает от:

  • XSS;
  • внедрения HTML;
  • исполнения скриптов.

Создание элементов с отключением autocomplete

Иногда требуется полный контроль над вводом.

new TomSelect('#tags', {

    create: true,

    render: {
        no_results: function() {
            return '<div>Ничего не найдено</div>';
        }
    }
});

Использование createFilter вместе с серверной проверкой

new TomSelect('#tags', {

    create: true,

    createFilter: function(input) {

        return fetch('/api/check?tag=' + input)
            .then(response => response.json())
            .then(data => data.allowed);
    }
});

На практике чаще используют синхронную локальную проверку, поскольку createFilter рассчитан прежде всего на мгновенный boolean-результат.


Управление созданием через API

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

const control = new TomSelect('#tags');

Создание option:

control.addOption({
    value: 'nodejs',
    text: 'Node.js'
});

Автоматический выбор:

control.addItem('nodejs');

Удаление созданных элементов

control.removeOption('nodejs');

Полная конфигурация создания элементов

new TomSelect('#skills', {

    createOnBlur: true,

    createFilter: function(input) {

        input = input.trim();

        return (
            input.length >= 2 &&
            input.length <= 20
        );
    },

    create: function(input) {

        input = input.trim();

        return {
            value: input.toLowerCase(),
            text: input,
            custom: true
        };
    },

    persist: false,

    render: {

        option_create: function(data, escape) {

            return `
                <div class="create">
                    Создать:
                    <strong>${escape(data.input)}</strong>
                </div>
            `;
        }
    }
});