Режим создания элементов позволяет добавлять новые значения, которых
нет в исходном списке <option> или в массиве данных.
Эта возможность особенно полезна при разработке систем тегов, форм ввода
категорий, email-адресов, навыков, ключевых слов и других динамических
интерфейсов.
По умолчанию Tom Select работает только с существующими значениями. Если пользователь вводит текст, отсутствующий среди доступных опций, выбрать его нельзя. Режим создания снимает это ограничение.
Для активации используется параметр create.
<select id="skills" multiple>
<option value="js">JavaScript</option>
<option value="php">PHP</option>
</select>
new TomSelect('#skills', {
create: true
});
После включения:
Когда пользователь вводит текст:
React
Tom Select:
Проверяет наличие совпадения среди опций.
Если совпадения нет — предлагает создать новый элемент.
После подтверждения:
При включённом create: true интерфейс автоматически
показывает строку:
Add React...
Текст зависит от введённого значения.
Основной сценарий:
new TomSelect('#tags', {
create: true
});
Поведение:
Enter;Наиболее распространённый вариант — режим множественного выбора.
<select id="tags" multiple></select>
new TomSelect('#tags', {
create: true
});
Теперь можно добавлять любое количество пользовательских значений.
Tom Select может работать не только с <select>, но
и с <input>.
<input id="emails">
new TomSelect('#emails', {
create: true,
persist: false
});
В этом режиме значения хранятся как элементы коллекции.
Вместо true можно передать функцию.
Это позволяет полностью контролировать структуру создаваемых объектов.
new TomSelect('#users', {
create: function(input) {
return {
value: input.toLowerCase(),
text: input
};
}
});
Функция получает введённую строку.
create: function(input) {
console.log(input);
}
Если пользователь ввёл:
Vue
то в input попадёт:
"Vue"
Функция должна вернуть объект option.
Минимальный вариант:
return {
value: input,
text: input
};
Можно добавлять собственные свойства.
new TomSelect('#tags', {
create: function(input) {
return {
value: input,
text: input,
custom: true,
createdAt: Date.now()
};
}
});
Эти поля затем доступны:
Часто требуется создавать ID автоматически.
new TomSelect('#items', {
create: function(input) {
return {
id: crypto.randomUUID(),
value: input,
text: input
};
},
valueField: 'value',
labelField: 'text'
});
Во многих проектах требуется нормализовать ввод.
create: function(input) {
return {
value: input.toLowerCase(),
text: input
};
}
create: function(input) {
return {
value: input.toUpperCase(),
text: input
};
}
create: function(input) {
input = input.trim();
return {
value: input,
text: input
};
}
new TomSelect('#tags', {
create: function(input) {
input = input.trim();
if (!input.length) {
return false;
}
return {
value: input,
text: input
};
}
});
Возврат false отменяет создание.
new TomSelect('#tags', {
create: function(input) {
if (input.length < 3) {
return false;
}
return {
value: input,
text: input
};
}
});
new TomSelect('#tags', {
create: function(input) {
if (!/^[a-z]+$/i.test(input)) {
return false;
}
return {
value: input,
text: input
};
}
});
new TomSelect('#emails', {
create: function(input) {
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!valid.test(input)) {
return false;
}
return {
value: input,
text: input
};
}
});
new TomSelect('#tags', {
create: function(input) {
const slug = input
.toLowerCase()
.replace(/\s+/g, '-');
return {
value: slug,
text: input
};
}
});
Параметр persist определяет, сохраняются ли созданные
элементы в списке опций.
new TomSelect('#tags', {
create: true,
persist: true
});
Поведение:
new TomSelect('#tags', {
create: true,
persist: false
});
Поведение:
Это особенно полезно для одноразовых тегов.
Позволяет создавать элемент автоматически при потере фокуса.
new TomSelect('#tags', {
create: true,
createOnBlur: true
});
Теперь необязательно нажимать Enter.
Позволяет определить, какие значения разрешено создавать.
new TomSelect('#tags', {
create: true,
createFilter: /^[A-Za-z0-9]+$/
});
new TomSelect('#tags', {
create: true,
createFilter: function(input) {
return input.length >= 3;
}
});
Только разрешает или запрещает создание.
Создаёт сам объект option.
Обычно они работают совместно.
new TomSelect('#tags', {
createFilter: input => input.length >= 3,
create: function(input) {
return {
value: input,
text: input
};
}
});
new TomSelect('#tags', {
create: true,
createFilter: function(input) {
input = input.toLowerCase();
return !this.options.hasOwnProperty(input);
}
});
Для настройки используется render.option_create.
new TomSelect('#tags', {
create: true,
render: {
option_create: function(data, escape) {
return `
<div class="create">
Создать тег:
<strong>${escape(data.input)}</strong>
</div>
`;
}
}
});
В render-функциях необходимо использовать escape.
Неправильно:
return `<div>${data.input}</div>`;
Правильно:
return `<div>${escape(data.input)}</div>`;
Это предотвращает XSS-атаки.
Часто используется совместно с delimiter.
new TomSelect('#tags', {
create: true,
delimiter: ','
});
Теперь ввод:
html,css,javascript
автоматически создаёт несколько элементов.
Создание можно совмещать с AJAX-загрузкой.
new TomSelect('#users', {
valueField: 'id',
labelField: 'name',
searchField: 'name',
create: true,
load: function(query, callback) {
fetch('/api/users?q=' + query)
.then(response => response.json())
.then(data => callback(data))
.catch(() => callback());
}
});
Нередко новый элемент должен сохраняться на сервере.
new TomSelect('#categories', {
create: function(input, callback) {
fetch('/api/categories', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: input
})
})
.then(response => response.json())
.then(category => {
callback({
value: category.id,
text: category.title
});
})
.catch(() => callback());
}
});
Tom Select поддерживает callback-механику.
create: function(input, callback) {
setTimeout(() => {
callback({
value: input,
text: input
});
}, 1000);
}
create: function(input, callback) {
callback();
}
или:
create: function(input, callback) {
callback(false);
}
Созданные элементы можно визуально выделять.
new TomSelect('#tags', {
create: function(input) {
return {
value: input,
text: input,
userCreated: true
};
},
render: {
option: function(data, escape) {
const cls = data.userCreated
? 'created'
: 'default';
return `
<div class="${cls}">
${escape(data.text)}
</div>
`;
}
}
});
После создания объект появляется в options.
const select = new TomSelect('#tags', {
create: true
});
console.log(select.options);
select.addOption({
value: 'nodejs',
text: 'Node.js'
});
select.addItem('nodejs');
select.addOption({
value: 'react',
text: 'React'
});
select.addItem('react');
new TomSelect('#tags', {
create: true,
maxItems: 5
});
new TomSelect('#tags', {
create: function(input) {
if (this.items.length >= 5) {
return false;
}
return {
value: input,
text: input
};
}
});
new TomSelect('#tags', {
plugins: ['remove_button'],
create: true
});
new TomSelect('#tags', {
plugins: ['drag_drop'],
create: true
});
Неправильно:
create: function(input) {
return input;
}
Правильно:
create: function(input) {
return {
value: input,
text: input
};
}
Если структура данных нестандартная:
{
id: 1,
title: 'Vue'
}
необходимо указать:
new TomSelect('#tags', {
valueField: 'id',
labelField: 'title'
});
Опасно:
return `<div>${data.text}</div>`;
Безопасно:
return `<div>${escape(data.text)}</div>`;
<input id="article-tags">
new TomSelect('#article-tags', {
plugins: ['remove_button'],
persist: false,
createOnBlur: true,
delimiter: ',',
createFilter: function(input) {
return input.length >= 2;
},
create: function(input) {
input = input.trim();
return {
value: input.toLowerCase(),
text: input
};
},
render: {
option_create: function(data, escape) {
return `
<div class="create-tag">
Новый тег:
<strong>${escape(data.input)}</strong>
</div>
`;
}
}
});