Обязательные поля

Обязательное поле — это элемент формы, который должен содержать выбранное значение перед отправкой данных. В стандартном HTML такая проверка реализуется атрибутом required, однако при использовании Tom Select появляется дополнительный уровень абстракции: исходный <select> скрывается, а управление переходит к JavaScript-компоненту.

Tom Select сохраняет совместимость с HTML-валидацией, но требует понимания особенностей работы с DOM, скрытыми элементами и пользовательским интерфейсом.

Базовый пример обязательного поля:

<form id="user-form">
    <select id="country" required>
        <option value="">Выберите страну</option>
        <option value="kz">Казахстан</option>
        <option value="ru">Россия</option>
        <option value="uz">Узбекистан</option>
    </select>

    <button type="submit">Отправить</button>
</form>
new TomSelect('#country');

В этом случае браузер не позволит отправить форму, пока значение не будет выбрано.


Как Tom Select работает с required

После инициализации библиотека:

  1. Скрывает оригинальный <select>
  2. Создаёт собственный DOM-интерфейс
  3. Синхронизирует выбранные значения
  4. Сохраняет исходный элемент формы

Атрибут required продолжает находиться на оригинальном элементе, поэтому браузерная валидация остаётся рабочей.

Пример структуры DOM после инициализации:

<select id="country" required hidden tabindex="-1">

Tom Select создаёт визуальный контейнер:

<div class="ts-wrapper">
    <div class="ts-control">
        <input type="text">
    </div>
</div>

При этом именно скрытый <select> участвует в отправке формы.


Обязательное одиночное поле

Наиболее распространённый сценарий — обязательный выбор одного значения.

HTML

<select id="city" required>
    <option value="">Выберите город</option>
    <option value="astana">Астана</option>
    <option value="almaty">Алматы</option>
    <option value="karaganda">Караганда</option>
</select>

JavaScript

new TomSelect('#city', {
    allowEmptyOption: true
});

Особенности

Ключевой момент — наличие пустого <option value="">.

Без него:

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

Неправильный вариант:

<select required>
    <option value="kz">Казахстан</option>
    <option value="ru">Россия</option>
</select>

Правильный вариант:

<select required>
    <option value="">Выберите страну</option>
    <option value="kz">Казахстан</option>
    <option value="ru">Россия</option>
</select>

Placeholder и обязательные поля

Placeholder помогает визуально обозначить, что значение ещё не выбрано.

new TomSelect('#country', {
    placeholder: 'Выберите страну'
});

Однако placeholder не заменяет пустой <option>.

Ошибочный подход:

<select required>
    <option value="kz">Казахстан</option>
</select>
new TomSelect('#country', {
    placeholder: 'Выберите страну'
});

В этом случае поле уже содержит значение kz.


Обязательные множественные поля

Для множественного выбора required работает иначе.

HTML

<select id="skills" multiple required>
    <option value="js">JavaScript</option>
    <option value="ts">TypeScript</option>
    <option value="react">React</option>
</select>

JavaScript

new TomSelect('#skills', {
    plugins: ['remove_button']
});

Особенности поведения

Для multiple браузер считает поле валидным, если выбран хотя бы один элемент.

Проверка работает автоматически:

form.checkValidity();

Если пользователь удалит все элементы:

select.tomselect.clear();

форма снова станет невалидной.


Проверка через JavaScript

Tom Select предоставляет API для получения текущих значений.

Проверка одиночного поля

const control = document.querySelector('#country').tomselect;

if (!control.getValue()) {
    console.log('Поле обязательно');
}

Проверка multiple-select

const skills = document.querySelector('#skills').tomselect;

if (skills.getValue().length === 0) {
    console.log('Нужно выбрать хотя бы один навык');
}

Использование checkValidity

Стандартная HTML-валидация совместима с Tom Select.

const form = document.querySelector('#form');

form.addEventListener('submit', (e) => {
    if (!form.checkValidity()) {
        e.preventDefault();
    }
});

Более современный вариант:

form.addEventListener('submit', (e) => {
    if (!form.reportValidity()) {
        e.preventDefault();
    }
});

Метод reportValidity():

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

Пользовательские сообщения об ошибках

Для обязательных полей часто требуется собственный текст ошибки.

Использование setCustomValidity

const select = document.querySelector('#country');

select.addEventListener('invalid', () => {
    select.setCustomValidity('Необходимо выбрать страну');
});

select.addEventListener('change', () => {
    select.setCustomValidity('');
});

Валидация через события Tom Select

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

Проверка при изменении значения

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

control.on('change', (value) => {
    if (!value) {
        console.log('Поле пустое');
    }
});

Проверка multiple-select

control.on('change', () => {
    const values = control.getValue();

    if (values.length === 0) {
        console.log('Выберите минимум один элемент');
    }
});

Подсветка обязательных полей

Часто требуется визуально показать ошибку.

Добавление CSS-класса

const select = document.querySelector('#country');
const wrapper = select.tomselect.wrapper;

function validate() {
    if (!select.value) {
        wrapper.classList.add('invalid');
    } else {
        wrapper.classList.remove('invalid');
    }
}

select.addEventListener('change', validate);

CSS

.invalid .ts-control {
    border-color: red;
}

Подсветка при отправке формы

Наиболее распространённый сценарий — проверка перед submit.

const form = document.querySelector('#form');

form.addEventListener('submit', (e) => {
    const select = document.querySelector('#country');
    const tom = select.tomselect;

    if (!tom.getValue()) {
        e.preventDefault();

        tom.wrapper.classList.add('invalid');
    }
});

Работа с пустыми значениями

Tom Select поддерживает пустые значения через allowEmptyOption.

new TomSelect('#country', {
    allowEmptyOption: true
});

Без этого параметра пустой <option> может игнорироваться.


Автоматическое снятие ошибки

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

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

control.on('change', (value) => {
    if (value) {
        control.wrapper.classList.remove('invalid');
    }
});

Обязательные поля с AJAX-загрузкой

При динамической загрузке данных обязательность поля сохраняется.

new TomSelect('#users', {
    valueField: 'id',
    labelField: 'name',
    searchField: 'name',

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

Особенности

Даже если список ещё не загружен:

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

Проверка после асинхронной загрузки

Иногда требуется запретить отправку формы, пока данные не загружены.

const control = new TomSelect('#users', {
    preload: true,

    load(query, callback) {
        fetch('/api/users')
            .then(r => r.json())
            .then(data => callback(data));
    }
});
form.addEventListener('submit', (e) => {
    if (control.loading > 0) {
        e.preventDefault();
    }
});

Минимальное количество элементов

HTML required проверяет только наличие хотя бы одного значения. Для более сложной логики нужна ручная проверка.

Минимум 2 элемента

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

form.addEventListener('submit', (e) => {
    const values = control.getValue();

    if (values.length < 2) {
        e.preventDefault();

        alert('Выберите минимум 2 навыка');
    }
});

Ограничение совместно с required

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

new TomSelect('#skills', {
    maxItems: 3
});

Логика:

  • минимум 1 элемент обязателен;
  • максимум 3 элемента разрешено.

Валидация через data-атрибуты

Удобный подход для больших форм.

HTML

<select id="country" data-required="true">

JavaScript

document.querySelectorAll('[data-required]')
    .forEach((element) => {

        const control = element.tomselect;

        if (!control.getValue()) {
            control.wrapper.classList.add('invalid');
        }
    });

Интеграция с Bootstrap

Tom Select можно интегрировать со стандартной системой валидации Bootstrap.

CSS-класс

if (!control.getValue()) {
    control.wrapper.classList.add('is-invalid');
}

CSS

.ts-wrapper.is-invalid .ts-control {
    border-color: #dc3545;
}

Интеграция с FormValidation

Пример ручной интеграции:

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

control.on('change', () => {
    validator.revalidateField('country');
});

Сброс обязательного поля

После очистки значение снова становится невалидным.

control.clear();

При использовании формы:

form.reset();

Tom Select необходимо синхронизировать:

control.clear();
control.sync();

Динамическое изменение обязательности

Атрибут required можно изменять программно.

Сделать поле обязательным

select.required = true;

Убрать обязательность

select.required = false;

Условно обязательные поля

Частый сценарий — поле обязательно только при определённых условиях.

checkbox.addEventListener('change', () => {

    if (checkbox.checked) {
        select.required = true;
    } else {
        select.required = false;
    }
});

Проверка при потере фокуса

Валидацию можно запускать после blur.

control.on('blur', () => {

    if (!control.getValue()) {
        control.wrapper.classList.add('invalid');
    }
});

Работа с create:true

При разрешении пользовательских значений обязательность сохраняется.

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

Поле считается заполненным:

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

Особенности required и disabled

Если поле отключено:

<select required disabled>

валидация не выполняется.

При динамическом включении:

select.disabled = false;

необходимо обновить состояние:

select.tomselect.enable();

Частые ошибки

Отсутствие пустого option

<select required>
    <option value="1">One</option>
</select>

Поле уже валидно.


Проверка визуального input вместо select

Неправильно:

document.querySelector('.ts-control input').value

Правильно:

select.tomselect.getValue()

Удаление required после инициализации

Некоторые разработчики ошибочно удаляют атрибут:

select.removeAttribute('required');

Это отключает встроенную HTML-валидацию.


Проверка только CSS-классов

Неправильно:

if (wrapper.classList.contains('has-items'))

Правильно:

if (control.getValue())

Полная схема обязательного поля

<form id="form">

    <select id="country" required>
        <option value="">Выберите страну</option>
        <option value="kz">Казахстан</option>
        <option value="ru">Россия</option>
        <option value="uz">Узбекистан</option>
    </select>

    <button type="submit">
        Отправить
    </button>

</form>
const selectElement = document.querySelector('#country');

const control = new TomSelect('#country', {
    allowEmptyOption: true,
    placeholder: 'Выберите страну'
});

const form = document.querySelector('#form');

form.addEventListener('submit', (e) => {

    if (!control.getValue()) {

        e.preventDefault();

        selectElement.setCustomValidity(
            'Необходимо выбрать страну'
        );

        control.wrapper.classList.add('invalid');

        selectElement.reportValidity();

    } else {

        selectElement.setCustomValidity('');

        control.wrapper.classList.remove('invalid');
    }
});

control.on('change', () => {

    if (control.getValue()) {

        selectElement.setCustomValidity('');

        control.wrapper.classList.remove('invalid');
    }
});
.invalid .ts-control {
    border: 1px solid red;
}