Валидация на стороне клиента при использовании Choices.js строится
вокруг контроля состояния выбранных значений, так как библиотека
заменяет стандартные элементы <select> и
<input> на управляемый JavaScript-компонент. Это
означает, что классические механизмы HTML5 частично перестают быть
достаточными, и логика проверки переносится в обработчики событий и слой
взаимодействия с формой.
Основная цель клиентской валидации в контексте Choices.js — обеспечить соответствие выбранных значений бизнес-правилам до отправки формы: количество элементов, допустимые значения, формат данных и ограничения по списку.
Choices.js не выполняет валидацию автоматически. Библиотека предоставляет только управление состоянием выбора, поэтому проверка строится на внешней логике:
Ключевая сущность — экземпляр Choices, содержащий текущее состояние выбора:
const choices = new Choices('#select', {
removeItemButton: true,
shouldSort: false
});
Получение выбранных значений:
const selected = choices.getValue(true);
Наиболее распространённый сценарий — требование выбрать хотя бы один элемент. В случае одиночного выбора это эквивалент проверки пустого значения, для множественного — длины массива.
function validateRequired(choicesInstance) {
const value = choicesInstance.getValue(true);
return Array.isArray(value) ? value.length > 0 : value !== '';
}
Привязка к форме:
form.addEventListener('submit', (e) => {
if (!validateRequired(choices)) {
e.preventDefault();
showError('Поле обязательно для заполнения');
}
});
Для мультивыборов часто задаются ограничения:
function validateRange(choicesInstance, min, max) {
const value = choicesInstance.getValue(true);
const count = value.length;
return count >= min && count <= max;
}
Использование:
if (!validateRange(choices, 2, 5)) {
showError('Необходимо выбрать от 2 до 5 элементов');
}
Choices.js позволяет отслеживать момент превышения лимита через события, но логика ограничения обычно реализуется вручную.
Choices.js предоставляет события, позволяющие выполнять проверку в реальном времени:
addItemremoveItemchangehighlightItemchoiceНаиболее важные для валидации — addItem и
removeItem.
choices.passedElement.element.addEventListener(
'addItem',
() => validateField()
);
choices.passedElement.element.addEventListener(
'removeItem',
() => validateField()
);
Реактивная функция проверки:
function validateField() {
const valid = validateRange(choices, 1, 3);
if (!valid) {
setFieldState('error');
} else {
setFieldState('valid');
}
}
Choices.js позволяет динамически добавлять элементы, что требует
защиты от недопустимых значений. Особенно актуально при использовании
createItems: true.
Проверка может включать:
const allowedPattern = /^[a-zA-Z0-9\s]{3,20}$/;
function validateItem(value) {
return allowedPattern.test(value);
}
При создании элемента:
choices.passedElement.element.addEventListener('addItem', (event) => {
const value = event.detail.value;
if (!validateItem(value)) {
choices.removeActiveItemsByValue(value);
showError('Недопустимый формат значения');
}
});
Хотя Choices.js предотвращает часть дублирования в стандартных режимах, при кастомных источниках данных дубликаты могут появляться.
function hasDuplicate(choicesInstance, value) {
const values = choicesInstance.getValue(true);
return values.includes(value);
}
Использование:
if (hasDuplicate(choices, newValue)) {
showError('Значение уже выбрано');
}
Choices.js не всегда синхронизирован с нативной валидацией формы, поэтому требуется ручная синхронизация состояния.
const input = document.querySelector('#select');
function syncValidity() {
if (!validateRequired(choices)) {
input.setCustomValidity('Ошибка выбора');
} else {
input.setCustomValidity('');
}
}
Привязка:
choices.passedElement.element.addEventListener('change', syncValidity);
Это позволяет использовать стандартный механизм браузера:
form.reportValidity();
Распространённый подход — централизованная проверка всех Choices-инстансов перед submit:
const fields = [choicesA, choicesB, choicesC];
function validateForm() {
return fields.every(c => validateRequired(c));
}
form.addEventListener('submit', (e) => {
if (!validateForm()) {
e.preventDefault();
showError('Проверьте корректность заполнения полей');
}
});
При работе с динамическими источниками данных может потребоваться проверка через сервер:
async function validateServer(value) {
const res = await fetch('/validate', {
method: 'POST',
body: JSON.stringify({ value }),
headers: { 'Content-Type': 'application/json' }
});
const data = await res.json();
return data.valid;
}
Использование с debounce:
let timer;
choices.passedElement.element.addEventListener('addItem', (e) => {
clearTimeout(timer);
timer = setTimeout(async () => {
const valid = await validateServer(e.detail.value);
if (!valid) {
choices.removeActiveItemsByValue(e.detail.value);
showError('Значение не проходит серверную проверку');
}
}, 300);
});
При включённой опции создания новых элементов важно контролировать ввод:
const choices = new Choices('#select', {
createItems: true,
duplicateItemsAllowed: false
});
Дополнительная логика:
choices.passedElement.element.addEventListener('addItem', (e) => {
const value = e.detail.value.trim();
if (value.length < 3) {
choices.removeActiveItemsByValue(value);
showError('Минимальная длина — 3 символа');
}
});
Choices.js не предоставляет встроенного UI для ошибок, поэтому применяется управление через DOM:
function setFieldState(state) {
const container = choices.containerOuter.element;
container.classList.remove('is-valid', 'is-error');
if (state === 'error') {
container.classList.add('is-error');
}
if (state === 'valid') {
container.classList.add('is-valid');
}
}
Пример отображения сообщения:
function showError(message) {
const errorBox = document.querySelector('.error-box');
errorBox.textContent = message;
}
При изменении значений через API важно запускать повторную проверку:
choices.setValue(['A', 'B']);
validateField();
syncValidity();
Игнорирование этого шага приводит к рассинхронизации UI и логики валидации.
Очистка состояния также должна учитывать правила:
choices.clearStore();
if (!validateRequired(choices)) {
setFieldState('error');
}
В реальных сценариях применяется набор условий одновременно:
function validateAll() {
const value = choices.getValue(true);
if (value.length === 0) return false;
if (value.length > 5) return false;
if (value.some(v => v.length < 2)) return false;
return true;
}
Часто валидация выносится в отдельный слой:
Такой подход позволяет масштабировать логику без привязки к конкретному экземпляру библиотеки.
const validators = {
required: (v) => v.length > 0,
max: (v, n) => v.length <= n
};
Валидация Choices.js на клиенте строится как реактивная система, где: