Механизм создания элементов в библиотеке Tom Select позволяет пользователю вводить значения, отсутствующие в списке опций, после чего эти значения автоматически превращаются в новые selectable-элементы. Такая возможность особенно полезна при работе с тегами, категориями, метками, адресами электронной почты, навыками, пользовательскими фильтрами и любыми динамическими наборами данных.
Создание элементов регулируется несколькими параметрами:
createcreateOnBlurcreateFilterpersistdelimiterБазовый пример:
<select id="tags" multiple>
<option value="javascript">JavaScript</option>
<option value="php">PHP</option>
</select>
new TomSelect('#tags', {
create: true
});
После включения create: true пользователь получает
возможность вводить новые значения вручную. При нажатии Enter библиотека
создаёт новый элемент и сразу добавляет его в список выбранных.
Наиболее распространённый вариант:
new TomSelect('#tags', {
create: true
});
В этом режиме:
value;Например, ввод:
TypeScript
создаёт:
{
value: 'TypeScript',
text: 'TypeScript'
}
Параметр create может принимать функцию, позволяющую
полностью контролировать структуру создаваемого объекта.
new TomSelect('#tags', {
create: function(input) {
return {
value: input.toLowerCase(),
text: input,
custom: true
};
}
});
Теперь ввод:
React
создаёт:
{
value: 'react',
text: 'React',
custom: true
}
Такой подход применяется для:
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 автоматически создаёт элемент при
потере фокуса.
new TomSelect('#tags', {
create: true,
createOnBlur: true
});
Поведение:
Особенно полезно для:
Параметр 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);
}
});
Подобная проверка полезна для:
Tom Select умеет автоматически предотвращать повторяющиеся элементы, однако дополнительная проверка часто используется вручную.
new TomSelect('#skills', {
create: true,
createFilter: function(input) {
const exists = this.options.hasOwnProperty(
input.toLowerCase()
);
return !exists;
}
});
Параметр persist определяет, остаются ли созданные
элементы в списке после удаления.
new TomSelect('#tags', {
create: true,
persist: false
});
Поведение:
| persist | Результат |
|---|---|
true |
элемент сохраняется |
false |
элемент удаляется после deselect |
new TomSelect('#tags', {
create: true,
persist: true
});
Сценарий:
new TomSelect('#tags', {
create: true,
persist: false
});
Сценарий:
Практический сценарий:
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()
};
}
});
Созданные элементы можно визуально выделять.
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'
}
]
});
Tom Select умеет автоматически разбивать ввод.
new TomSelect('#tags', {
create: true,
delimiter: ','
});
Ввод:
HTML,CSS,JavaScript
создаёт три отдельных элемента.
При вставке текста Tom Select может автоматически создавать элементы.
new TomSelect('#skills', {
create: true,
delimiter: ','
});
Вставка:
Vue,React,Svelte
создаёт:
Часто требуется очистка значения.
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
});
Если сервер не находит значение, пользователь может создать собственное.
new TomSelect('#tags', {
create: true,
maxItems: 5
});
После достижения лимита создание блокируется автоматически.
Tom Select сразу выбирает созданный элемент. Это стандартное поведение:
new TomSelect('#tags', {
create: true
});
После создания:
<select id="country">
<option value="kz">Казахстан</option>
<option value="ru">Россия</option>
</select>
new TomSelect('#country', {
create: true
});
Теперь пользователь может вводить собственные страны.
<input id="skills">
new TomSelect('#skills', {
create: true,
delimiter: ','
});
Такой режим часто используется для:
При создании нового элемента Tom Select выполняет последовательность:
createFilter;create;addOption;addItem;Понимание этого цикла важно при интеграции:
new TomSelect('#tags', {
create: true,
onItemAdd: function(value) {
console.log('Добавлен:', value);
}
});
new TomSelect('#tags', {
create: true,
onOptionAdd: function(value, data) {
console.log(data);
}
});
Событие вызывается именно при добавлении новой option.
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() защищает от:
Иногда требуется полный контроль над вводом.
new TomSelect('#tags', {
create: true,
render: {
no_results: function() {
return '<div>Ничего не найдено</div>';
}
}
});
new TomSelect('#tags', {
create: true,
createFilter: function(input) {
return fetch('/api/check?tag=' + input)
.then(response => response.json())
.then(data => data.allowed);
}
});
На практике чаще используют синхронную локальную проверку, поскольку
createFilter рассчитан прежде всего на мгновенный
boolean-результат.
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>
`;
}
}
});