HTML5 предоставляет встроенную систему проверки форм: обязательные
поля, ограничения длины, регулярные выражения, типы данных и
пользовательские сообщения об ошибках. При интеграции с Tom Select
возникает важная особенность: библиотека скрывает оригинальный
<select> или <input> и создаёт
собственный интерфейс поверх стандартного элемента. Из-за этого
поведение нативной валидации начинает отличаться от обычных
HTML-форм.
Понимание того, как Tom Select взаимодействует с HTML5 Validation API, позволяет избежать проблем с отправкой форм, отображением ошибок и синхронизацией состояния элементов.
HTML5-валидация основывается на свойствах и атрибутах DOM-элементов:
| Атрибут | Назначение |
|---|---|
required |
Поле обязательно |
pattern |
Проверка по регулярному выражению |
minlength / maxlength |
Ограничение длины |
type="email" |
Проверка email |
multiple |
Разрешение нескольких значений |
Методы Validation API:
element.checkValidity()
Проверяет поле и возвращает true или
false.
element.reportValidity()
Показывает встроенное сообщение браузера.
element.setCustomValidity(message)
Устанавливает собственную ошибку.
Стандартный <select>:
<form id="form">
<select id="country" required>
<option value="">Выберите страну</option>
<option value="kz">Казахстан</option>
<option value="ru">Россия</option>
</select>
<button type="submit">Отправить</button>
</form>
Подключение Tom Select:
new TomSelect('#country');
После инициализации библиотека скрывает исходный элемент:
<select hidden tabindex="-1">
Однако оригинальный <select> остаётся частью
формы, поэтому HTML5-валидация продолжает работать.
Хотя браузер валидирует скрытый <select>,
визуальное сообщение может отображаться некорректно:
Особенно часто это происходит в:
Наиболее стабильный подход — валидировать форму вручную.
const form = document.querySelector('#form');
form.addEventListener('submit', (e) => {
if (!form.checkValidity()) {
e.preventDefault();
form.reportValidity();
}
});
Tom Select продолжит синхронизировать значение с оригинальным
<select>.
HTML:
<select id="skills" multiple required>
<option value="js">JavaScript</option>
<option value="ts">TypeScript</option>
<option value="vue">Vue</option>
</select>
Tom Select:
new TomSelect('#skills');
HTML5 считает поле валидным только тогда, когда выбран минимум один элемент.
Проверка:
const select = document.querySelector('#skills');
console.log(select.checkValidity());
Если список пуст:
false
HTML5 не умеет ограничивать минимальное число выбранных значений в
<select multiple>.
Для этого используется пользовательская валидация.
const select = document.querySelector('#skills');
select.addEventListener('change', () => {
if (select.selectedOptions.length < 2) {
select.setCustomValidity('Выберите минимум 2 навыка');
} else {
select.setCustomValidity('');
}
});
Tom Select предоставляет собственные события:
const control = new TomSelect('#skills');
control.on('change', () => {
const select = control.input;
if (select.selectedOptions.length < 2) {
select.setCustomValidity('Минимум 2 значения');
} else {
select.setCustomValidity('');
}
});
control.input — ссылка на оригинальный DOM-элемент.
Для немедленного показа ошибки:
control.on('change', () => {
const select = control.input;
if (select.selectedOptions.length < 2) {
select.setCustomValidity('Недостаточно элементов');
select.reportValidity();
} else {
select.setCustomValidity('');
}
});
Tom Select может работать поверх <input>.
<input id="email" type="email" required>
new TomSelect('#email', {
create: true
});
В этом случае HTML5-валидация типа email продолжает
работать:
document.querySelector('#email').checkValidity();
При использовании:
create: true
пользователь может вводить произвольные значения.
Если поле имеет:
type="email"
браузер проверит строку только при отправке формы.
Пример:
<input id="users" type="email" multiple>
Tom Select:
new TomSelect('#users', {
create: true
});
Допустимый ввод:
user@example.com
Недопустимый:
invalid-email
Для проверки во время ввода:
const emailInput = document.querySelector('#users');
const control = new TomSelect('#users', {
create: true
});
control.on('item_add', () => {
if (!emailInput.checkValidity()) {
emailInput.setCustomValidity('Некорректный email');
emailInput.reportValidity();
} else {
emailInput.setCustomValidity('');
}
});
HTML:
<input
id="username"
pattern="[a-zA-Z0-9_]+"
required
>
Tom Select:
new TomSelect('#username', {
create: true
});
Теперь браузер автоматически проверяет:
_.Иногда требуется немедленная валидация:
const input = document.querySelector('#username');
const regex = /^[a-zA-Z0-9_]+$/;
const control = new TomSelect('#username', {
create: true
});
control.on('item_add', (value) => {
if (!regex.test(value)) {
input.setCustomValidity(
'Разрешены только буквы, цифры и _'
);
input.reportValidity();
} else {
input.setCustomValidity('');
}
});
HTML5 использует псевдокласс:
:invalid
Однако Tom Select визуально отображает собственный контейнер, а не оригинальный элемент.
Поэтому:
select:invalid
не влияет на UI библиотеки.
Использование класса:
const control = new TomSelect('#country');
const select = control.input;
select.addEventListener('invalid', () => {
control.wrapper.classList.add('invalid');
});
CSS:
.ts-wrapper.invalid {
border: 1px solid red;
}
Удаление ошибки:
control.on('change', () => {
if (select.checkValidity()) {
control.wrapper.classList.remove('invalid');
}
});
Bootstrap использует классы:
.is-valid
.is-invalid
Интеграция:
const control = new TomSelect('#country');
const select = control.input;
select.addEventListener('invalid', () => {
control.wrapper.classList.add('is-invalid');
});
Очистка:
control.on('change', () => {
if (select.checkValidity()) {
control.wrapper.classList.remove('is-invalid');
control.wrapper.classList.add('is-valid');
}
});
Validation API позволяет создавать сложные правила.
Пример ограничения:
const control = new TomSelect('#tags', {
create: true
});
const select = control.input;
control.on('change', () => {
const values = control.items;
const forbidden = values.includes('admin');
if (forbidden) {
select.setCustomValidity(
'Тег admin запрещён'
);
} else {
select.setCustomValidity('');
}
});
Tom Select сам умеет запрещать дубли:
duplicates: false
Но иногда требуется дополнительная логика:
control.on('item_add', () => {
const values = control.items;
const unique = new Set(values);
if (unique.size !== values.length) {
select.setCustomValidity(
'Обнаружены дубликаты'
);
} else {
select.setCustomValidity('');
}
});
Проверка доступности имени:
const control = new TomSelect('#login', {
create: true
});
const input = control.input;
control.on('change', async (value) => {
const response = await fetch(`/check-login?value=${value}`);
const result = await response.json();
if (!result.available) {
input.setCustomValidity(
'Логин уже занят'
);
input.reportValidity();
} else {
input.setCustomValidity('');
}
});
Если поле невалидно:
form.addEventListener('submit', (e) => {
if (!form.checkValidity()) {
e.preventDefault();
form.reportValidity();
}
});
Это работает корректно даже с несколькими экземплярами Tom Select.
Проверка после потери фокуса:
control.control_input.addEventListener('blur', () => {
if (!select.checkValidity()) {
control.wrapper.classList.add('invalid');
}
});
control.control_input — внутреннее текстовое поле Tom
Select.
Автоматическое снятие invalid-состояния:
control.on('type', () => {
select.setCustomValidity('');
control.wrapper.classList.remove('invalid');
});
HTML5 игнорирует отключённые поля:
<select disabled required>
Tom Select синхронизирует это состояние автоматически:
control.disable();
Повторное включение:
control.enable();
HTML:
<input
id="tags"
maxlength="20"
>
Tom Select:
new TomSelect('#tags', {
create: true
});
HTML5 проверяет длину значения, но при multiple-режиме часто требуется ограничение количества элементов, а не длины строки.
Tom Select предоставляет встроенную настройку:
maxItems: 3
Пример:
new TomSelect('#skills', {
maxItems: 3
});
Это ограничение работает независимо от HTML5 Validation API.
Наиболее стабильный подход:
Использовать HTML5-атрибуты:
requiredpatterntypemaxlengthИспользовать Tom Select для UI.
Добавлять setCustomValidity() для сложной
логики.
Валидировать форму через:
form.checkValidity()HTML:
<form id="user-form">
<select
id="skills"
multiple
required
>
<option value="js">JavaScript</option>
<option value="ts">TypeScript</option>
<option value="vue">Vue</option>
<option value="react">React</option>
</select>
<button type="submit">
Отправить
</button>
</form>
Jav * aScript:
const form = document.querySelector('#user-form');
const control = new TomSelect('#skills', {
maxItems: 5
});
const select = control.input;
control.on('change', () => {
if (control.items.length < 2) {
select.setCustomValidity(
'Выберите минимум 2 технологии'
);
control.wrapper.classList.add('invalid');
} else {
select.setCustomValidity('');
control.wrapper.classList.remove('invalid');
}
});
form.addEventListener('submit', (e) => {
if (!form.checkValidity()) {
e.preventDefault();
form.reportValidity();
}
});
CSS:
.ts-wrapper.invalid {
border: 1px solid #d00;
}
Причина:
Решение:
setCustomValidity();Причина:
checkValidity() отдельно не вызывает invalid UI.Решение:
reportValidity()
Причина:
<option value="">;Правильно:
<option value=""></option>
Если форма валидируется раньше создания Tom Select:
form.checkValidity();
то состояние UI ещё не синхронизировано.
Правильный порядок:
const control = new TomSelect(...);
form.checkValidity();
Правильный подход:
control.input
Неправильный:
control.items
для основной валидации формы.
HTML5 Validation API работает только с DOM-элементом.
Хорошая практика:
function validateSkills(control) {
const select = control.input;
if (control.items.length < 2) {
select.setCustomValidity(
'Минимум 2 значения'
);
} else {
select.setCustomValidity('');
}
}
Лучше разделять:
Пример:
function showError(control) {
control.wrapper.classList.add('invalid');
}
function clearError(control) {
control.wrapper.classList.remove('invalid');
}
Особенности HTML5-валидации и Tom Select:
| Браузер | Особенности |
|---|---|
| Chrome | Наиболее стабильная работа |
| Firefox | Возможны проблемы с popup ошибки |
| Safari | Часто некорректный фокус |
| Mobile Safari | Нестабильное позиционирование tooltip |
| Edge | Поведение близко к Chrome |
Для сложных интерфейсов часто используется полностью кастомная система ошибок вместо встроенных browser tooltips.