Валидация форм в JavaScript
Валидация — это процесс проверки данных на соответствие заданным правилам, требованиям и ограничениям, который выполняется для обеспечения целостности, безопасности и корректности информации, поступающей в приложение, прежде чем она будет использована в бизнес-логике, сохранена в базе данных или передана другим системам. Валидация охватывает широкий спектр проверок, включая обязательность полей, соответствие типам данных, форматам, длине строк, допустимому диапазону значений, уникальность, а также более сложные бизнес-правила, такие как взаимосвязь между полями, проверка возраста или соответствие определенным паттернам, например, электронной почте или номеру телефона. Валидация может выполняться на разных уровнях приложения, включая клиентскую часть для мгновенной обратной связи с пользователем, серверную часть для обеспечения безопасности и целостности данных, а также на уровне базы данных для гарантии структурной согласованности. Эффективная валидация является критически важным аспектом разработки программного обеспечения, поскольку она предотвращает множество проблем, включая ошибки выполнения, повреждение данных, атаки на безопасность, такие как инъекции SQL и межсайтовый скриптинг, а также улучшает пользовательский опыт, предоставляя четкие и понятные сообщения об ошибках при неправильном вводе. В современной веб-разработке валидация часто реализуется с использованием как встроенных браузерных механизмов, так и пользовательских функций и библиотек, причем лучшие практики рекомендуют использовать валидацию на обоих уровнях — клиентском для удобства пользователя и серверном для безопасности и надежности.
Валидация форм — это специализированный процесс проверки данных, введенных пользователем в поля веб-формы, который выполняется для обеспечения того, что все обязательные поля заполнены, данные соответствуют ожидаемым форматам и правилам, и форма может быть безопасно отправлена на сервер для дальнейшей обработки. Валидация форм в браузере может быть реализована с использованием встроенных HTML-атрибутов, таких как required для обязательных полей, type для указания типа данных, minlength и maxlength для ограничения длины, pattern для задания регулярного выражения, min и max для числовых диапазонов, а также с использованием JavaScript для более сложной кастомной логики проверки. Современные браузеры предоставляют встроенную валидацию форм через API проверки достоверности, который позволяет разработчику получать доступ к состоянию валидации каждого поля через объект validity и свойство valid, а также управлять отображением стандартных сообщений об ошибках через метод setCustomValidity. Валидация форм на клиентской стороне обеспечивает мгновенную обратную связь пользователю, подсвечивая некорректные поля и показывая понятные сообщения об ошибках без необходимости отправки данных на сервер, что значительно улучшает пользовательский опыт и снижает нагрузку на сервер. Однако клиентская валидация никогда не должна рассматриваться как замена серверной валидации, поскольку она может быть обойдена злоумышленником или отключена в браузере, поэтому критически важно дублировать все проверки на серверной стороне для обеспечения безопасности и целостности данных.
Два уровня проверки
Браузер умеет проверять поля до отправки на сервер:
- HTML —
required,minlength,type="email",type="date",pattern,min/max(см. типы<input>и справочник HTML). - JavaScript — Constraint Validation API — читать состояние поля, показывать свои сообщения, блокировать
submit.
Серверная проверка остаётся обязательной: клиентский код можно обойти. Виды проверок и примеры — Проверка и валидация.
Серверная проверка — это процесс валидации данных, выполняемый на серверной стороне приложения после получения запроса от клиента, который является обязательным и критически важным элементом безопасности, поскольку он обеспечивает, что все поступающие данные проверены на соответствие правилам независимо от того, прошли ли они клиентскую валидацию, и что злонамеренные или поврежденные данные не будут обработаны или сохранены. Серверная проверка включает все те же типы проверок, что и клиентская валидация, но при этом она выполняется в доверенной среде, где данные могут быть проверены с полной уверенностью, включая проверку на уникальность в базе данных, существование связанных записей, проверку прав доступа, и другие проверки, требующие доступа к серверным ресурсам. Важным аспектом серверной проверки является тщательная санитизация и экранирование данных для предотвращения атак, таких как SQL-инъекции, межсайтовый скриптинг и межсайтовая подделка запросов, а также проверка данных на соответствие бизнес-правилам и ограничениям предметной области. Серверная проверка обычно возвращает клиенту структурированный ответ с описанием всех обнаруженных ошибок, позволяя интерфейсу отобразить пользователю конкретные сообщения для каждого некорректного поля, при этом важно предоставлять достаточно информации для исправления ошибок, но не раскрывать внутренние детали системы, которые могут быть использованы злоумышленниками. В современной веб-разработке серверная проверка часто реализуется с использованием специализированных библиотек и фреймворков, которые предоставляют декларативные способы описания правил валидации, автоматическую генерацию сообщений об ошибках и интеграцию с системами логирования и мониторинга.
Связь: события форм, работа с DOM, регулярные выражения для сложных pattern, готовые шаблоны для pattern, чтение и загрузка файлов.
Свойства полей по типу <input>
Разметка и HTML-атрибуты — в типах <input> и справочнике HTML. Ниже — что читает и меняет JavaScript у каждого типа поля.
Общие свойства элементов формы
| Свойство / метод | Назначение |
|---|---|
value | текущее значение поля |
defaultValue | значение из HTML при загрузке страницы |
required, disabled, readOnly | обязательность, блокировка, только чтение |
form | ссылка на родительский <form> (поле может быть вне тега, если указан атрибут form="id") |
type | тип элемента (text, checkbox, …) |
autofocus | фокус при загрузке (только один такой элемент на странице) |
validity, validationMessage, willValidate | состояние проверки — см. объект validity |
focus(), blur(), select() | фокус, снятие фокуса, выделение текста в поле |
Таблица по типам <input>
type | Ключевые свойства и методы в JS |
|---|---|
text, email, url, tel, search, password | value, minLength, maxLength, placeholder, pattern, selectionStart / selectionEnd, select() |
number, range | value, min, max, step, stepUp(n?), stepDown(n?) |
date, time, datetime-local, month, week | value в формате ISO (2026-05-30, 14:30, …), min, max, step |
color | value как #RRGGBB (например #ff0000) |
checkbox | checked, defaultChecked, indeterminate (промежуточное состояние "частично выбрано") |
radio | checked; группа полей с одним name — через document.querySelectorAll('input[name="gender"]') |
file | files (FileList), multiple, accept; у каждого File: name, size, type, lastModified |
hidden | только value |
submit, reset, button, image | value; у submit/image также formAction, formMethod, formEnctype |
const qty = document.querySelector('#qty');
qty.stepUp(); // +step (по умолчанию 1)
const avatar = document.querySelector('#avatar');
for (const file of avatar.files) {
console.log(file.name, file.size);
}
const agree = document.querySelector('#agree');
agree.indeterminate = true; // «частично выбрано» в UI
textarea и select
| Элемент | Дополнительно в JS |
|---|---|
textarea | value, rows, cols, wrap (soft / hard / off), spellcheck; Enter по умолчанию — новая строка, не submit |
select | value — значение выбранного <option>; selectedIndex; options — коллекция пунктов; multiple — массив через selectedOptions |
События полей — input (каждый символ), change (после commit — blur у text, сразу у select/checkbox), focus / blur, invalid — в событиях форм.
UX-практики валидации форм
Пользователь ожидает предсказуемую обратную связь:
- сообщение об ошибке рядом с полем, а не одно общее "данные неверны";
- фокус на первом невалидном поле после submit;
- снятие подсказки после исправления без повторной отправки, где это уместно.
Constraint Validation API (ниже, объект validity) закрывает эти требования без тяжёлых UI-библиотек.
Объект validity
Объект validity — это встроенный объект в браузерном API валидации форм, который является свойством каждого элемента формы, поддерживающего валидацию, и содержит набор булевых флагов, каждый из которых указывает на наличие определенного типа ошибки валидации для данного поля, предоставляя разработчику детальную информацию о том, почему поле считается недействительным. Свойства объекта validity включают valueMissing, которое указывает, что обязательное поле не заполнено, typeMismatch для несоответствия типу, patternMismatch для несоответствия регулярному выражению, tooLong и tooShort для нарушения ограничений длины, rangeUnderflow и rangeOverflow для выхода за числовые пределы, stepMismatch для несоответствия шагу числового поля, badInput для некорректного ввода, и customError, которое устанавливается при использовании метода setCustomValidity для кастомной проверки. Объект validity также содержит свойство valid, которое является обобщающим флагом, равным true только в том случае, если ни одна из ошибок не обнаружена, что позволяет быстро проверить, прошел ли элемент валидацию, без необходимости проверять каждый флаг отдельно. Использование объекта validity позволяет разработчику создавать детализированные и настраиваемые сообщения об ошибках, адаптированные к конкретному типу проблемы, а также реализовывать сложную логику валидации, зависящую от состояния других полей, или группировать несколько условий в одну проверку. Благодаря объекту validity разработчики могут полностью контролировать процесс валидации форм, сохраняя при этом встроенную поддержку браузера, что делает его мощным инструментом для создания надежных и удобных интерфейсов с мгновенной и понятной обратной связью для пользователя.
У каждого поля формы (HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement) есть свойство validity — набор флагов, почему значение не прошло проверку:
| Свойство | Когда true |
|---|---|
valueMissing | пустое обязательное поле |
typeMismatch | не подходит type (email, url…) |
patternMismatch | не совпало с pattern |
tooShort / tooLong | длина вне minlength / maxlength |
rangeUnderflow / rangeOverflow | число или дата вне min / max |
stepMismatch | не кратно step |
badInput | браузер не может прочитать значение |
customError | вы вызвали setCustomValidity с непустой строкой |
valid | все проверки пройдены |
const email = document.querySelector('#email');
email.addEventListener('input', () => {
if (email.validity.typeMismatch) {
console.log('Нужен адрес вида user@example.com');
}
});
Разбор:
querySelector('#email')получает ссылку на поле поidи даёт доступ к его API валидации.- Событие
inputсрабатывает на каждое изменение значения, поэтому проверка выполняется "на лету". email.validity.typeMismatchпроверяет, соответствует ли значение типу поля (type="email").- Такой обработчик подходит для моментальных подсказок без ожидания
submit. - Подход уменьшает количество неудачных отправок формы и ускоряет исправление ошибок пользователем.
Основные методы
checkValidity()
Возвращает true, если поле (или форма) валидно. Не показывает встроенный "пузырь" браузера.
if (!form.checkValidity()) {
// подсветить ошибки своим UI
}
Разбор:
checkValidity()запускает все встроенные правила HTML для формы и возвращает логический результат.- Знак
!инвертирует условие, поэтому ветка выполняется только при наличии хотя бы одной ошибки. - Метод не показывает стандартные сообщения браузера, что удобно для полностью кастомного интерфейса.
- В этом месте обычно включают подсветку полей, список ошибок и перевод фокуса на первое проблемное поле.
reportValidity()
Как checkValidity, но при false браузер показывает стандартное сообщение у первого невалидного поля (если не отключено CSS).
form.addEventListener('submit', (event) => {
if (!form.reportValidity()) {
event.preventDefault();
}
});
Разбор:
- Обработчик
submitперехватывает отправку в момент нажатия кнопки или вызоваrequestSubmit(). reportValidity()совмещает проверку и показ нативного сообщения у первого невалидного поля.- Если форма невалидна,
preventDefault()отменяет отправку данных на сервер. - Такая конструкция сохраняет нативный UX браузера и требует минимум кода для базовой валидации.
- Переменная
eventдаёт контроль над поведением формы в текущем цикле событий.
setCustomValidity(message)
Задаёт свою причину ошибки. Пустая строка '' снимает customError.
const password = document.querySelector('#password');
const confirm = document.querySelector('#confirm');
function validatePair() {
if (confirm.value && confirm.value !== password.value) {
confirm.setCustomValidity('Пароли не совпадают');
} else {
confirm.setCustomValidity('');
}
}
password.addEventListener('input', validatePair);
confirm.addEventListener('input', validatePair);
Разбор:
- Функция
validatePairинкапсулирует правило "подтверждение равно паролю" в одном месте. - Условие
confirm.value && confirm.value !== password.valueпроверяет только заполненное поле подтверждения. setCustomValidity('Пароли не совпадают')устанавливает кастомную ошибку и переводитvalidity.customErrorвtrue.setCustomValidity('')обязательно очищает ошибку после исправления данных.- Подписка на
inputу обоих полей даёт синхронную реакцию при изменении любой стороны пары.
Правило — после каждого изменения, влияющего на правило, снова вызывайте setCustomValidity('') или с новым текстом.
События
| Событие | Когда |
|---|---|
invalid | поле не прошло проверку при submit (можно preventDefault на кастомный UI) |
input | значение меняется — удобно снимать ошибку "на лету" |
change | значение зафиксировано (select, checkbox после выбора) |
field.addEventListener('invalid', (event) => {
event.preventDefault(); // отключить стандартный bubble
showError(field, field.validationMessage);
});
Разбор:
- Событие
invalidсрабатывает, когда конкретное поле не проходит проверку при отправке или вызовеreportValidity. preventDefault()отключает стандартный всплывающий bubble браузера, если вы рисуете свой UI.field.validationMessageвозвращает готовый текст ошибки, включая сообщения изsetCustomValidity.- Вызов
showError(...)выносит рендер ошибки в отдельную функцию и упрощает повторное использование. - Такой подход помогает сделать единый стиль сообщений для всех полей формы.
validationMessage — текст, который показал бы браузер (учитывает setCustomValidity).
Связка с CSS
В псевдоклассах CSS используют :user-invalid / :invalid для подсветки:
input:user-invalid {
border-color: #c0392b;
outline: 2px solid rgba(192, 57, 43, 0.3);
}
Разбор:
- Псевдокласс
:user-invalidприменяет стиль только после взаимодействия пользователя с полем. border-colorзадаёт заметный цвет рамки для быстрого визуального обнаружения ошибки.outlineдобавляет внешний контур, который лучше читается на разных фоновых цветах.- Такая комбинация улучшает доступность интерфейса и поддерживает понятный визуальный фидбек.
:user-invalid срабатывает после взаимодействия пользователя — меньше "красных полей" при первой загрузке страницы.
Пример — форма с единым блоком ошибок
<form id="signup" novalidate>
<!-- novalidate — только наш или смешанный сценарий; иначе двойные сообщения -->
<label>
Логин
<input name="login" required minlength="3" id="login">
</label>
<p id="login-error" hidden></p>
<button type="submit">Создать</button>
</form>
Разбор:
- Атрибут
novalidateотключает нативные всплывающие сообщения, чтобы полностью контролировать UX из JavaScript. requiredиminlength="3"продолжают задавать правила, даже если визуализация ошибок кастомная.id="login-error"создаёт отдельный узел для текстовой подсказки рядом с полем.- Кнопка
type="submit"запускает стандартный цикл отправки формы и обработчикsubmit. - Такая структура даёт хороший баланс между семантикой HTML и гибкостью клиентской логики.
Код ITЗагрузка примера кода…
Разбор:
- Обработчик
inputочищает старую ошибку и сразу прячет блок с сообщением после правки поля. - Проверка
form.checkValidity()вsubmitдаёт единый вход для всех ограничений HTML. - При ошибке код показывает конкретный текст
validationMessage, снимаетhiddenи переводит фокус на проблемное поле. returnзавершает обработчик, чтобы не выполнять отправку при невалидном состоянии.- Комментарий в конце указывает две стратегии отправки: через
fetchили стандартныйform.submit().
Атрибут novalidate на <form> отключает встроенный UI браузера — полезно, если сообщения рисуете вы сами. Без него можно вызывать reportValidity() точечно.
Асинхронная проверка (логин занят)
Асинхронная проверка — это процесс валидации данных, который требует выполнения асинхронных операций, таких как сетевые запросы к серверу или обращение к внешним API, для проверки условий, которые не могут быть определены только на основе локальных данных, например, проверка уникальности имени пользователя или электронной почты в базе данных, проверка существования связанных записей или верификация кода подтверждения. При асинхронной проверке пользовательский интерфейс должен оставаться отзывчивым и показывать индикатор загрузки, чтобы пользователь понимал, что проверка выполняется, а результаты проверки должны корректно обрабатываться, включая отображение сообщений об ошибках, если проверка не прошла, или разрешение отправки формы, если все условия выполнены. Важным аспектом асинхронной проверки является управление состоянием гонки, когда пользователь может изменить значение поля до завершения предыдущей проверки, поэтому необходимо использовать механизмы отмены запросов или игнорирования устаревших ответов, чтобы гарантировать, что отображаются только актуальные результаты проверки. Примером асинхронной проверки является проверка занятости логина при регистрации, где при вводе пользователем желаемого имени пользователя отправляется запрос к серверу, и на основе ответа отображается сообщение о том, доступен ли этот логин или уже занят, при этом все предыдущие запросы должны быть отменены при вводе новых символов, чтобы избежать путаницы. Асинхронная проверка значительно улучшает пользовательский опыт, предоставляя мгновенную обратную связь о доступности или корректности данных до отправки формы, но требует тщательного проектирования для обеспечения производительности, надежности и безопасности, а также для предотвращения избыточной нагрузки на сервер.
Встроенный API синхронный. Проверка "логин свободен на сервере" делается так:
- На
submit—preventDefault. fetchна API.- При конфликте —
field.setCustomValidity('Логин занят')иfield.reportValidity(). - При успехе —
form.requestSubmit()или отправка данных.
Не вызывайте setCustomValidity с текстом до ответа сервера на каждый input без debounce — иначе лишние запросы.
Отправка данных — FormData
FormData — это встроенный браузерный объект, который позволяет легко создавать набор пар ключ-значение, представляющих поля формы и их значения, и который особенно удобен для отправки данных форм на сервер с использованием Fetch API или XMLHttpRequest, автоматически кодируя данные в multipart/form-data формат, что необходимо для загрузки файлов. Объект FormData создается с помощью конструктора new FormData, который может принимать опционально HTML-элемент формы для автоматической инициализации всеми полями этой формы, или может быть создан пустым, и затем заполнен с помощью методов append, set и delete для добавления или удаления полей. FormData поддерживает добавление не только текстовых данных, но и файлов, которые могут быть выбраны пользователем через input типа file, включая доступ к самому объекту File, что позволяет передавать бинарные данные без необходимости вручную читать их или преобразовывать в строки. При использовании FormData с fetch не требуется явно указывать заголовок Content-Type, поскольку браузер автоматически устанавливает правильный заголовок с границей multipart/form-data, что упрощает код и гарантирует корректную отправку как простых полей, так и файлов. FormData также может быть использован для программной отправки данных, например, для реализации динамических форм или для отправки данных без фактического наличия HTML-формы на странице, и его можно легко преобразовать в обычный объект или JSON для отладки, хотя для этого потребуется ручной перебор пар ключ-значение. FormData является важным инструментом для создания современных веб-приложений, упрощая отправку данных форм на сервер и обеспечивая единообразную обработку как текстовых полей, так и загружаемых файлов.
После успешной проверки форму обычно отправляют на сервер. FormData собирает пары "имя поля → значение" из разметки, в том числе файлы:
Код ITЗагрузка примера кода…
Разбор:
event.preventDefault()отменяет стандартную отправку, чтобы сначала выполнить клиентскую проверку и асинхронныйfetch.- Блок
if (!form.checkValidity())сreportValidity()останавливает поток до сети, если локальные правила нарушены. new FormData(form)собирает данные поnameиз формы и автоматически включает файлы.body.append('source', 'web')добавляет техническое поле, которого нет в HTML.- В
fetchне нужно вручную задаватьContent-TypeдляFormData: браузер сам сформирует корректныйmultipart/form-dataс boundary. - Проверка
response.okотделяет успешный HTTP-ответ от ошибок сервера и упрощает обработку исключений.
Полезные методы:
| Метод | Назначение |
|---|---|
new FormData(form) | Снимок всех полей формы с атрибутом name |
append(name, value) | Добавить или продублировать ключ |
get(name) / getAll(name) | Прочитать одно или все значения |
delete(name) | Убрать ключ перед отправкой |
entries() | Итерация для отладки или ручной сборки |
Файлы: <input type="file" name="avatar"> попадает в FormData как File. Несколько файлов — getAll('avatar').
Если API ждёт JSON, а не multipart, соберите объект вручную:
const payload = Object.fromEntries(new FormData(form));
await fetch('/api/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
Разбор:
Object.fromEntries(new FormData(form))превращает пары из формы в обычный объект JavaScript.JSON.stringify(payload)сериализует объект в строку для передачи по HTTP как JSON.- Заголовок
Content-Type: application/jsonсообщает серверу формат тела запроса. - Этот вариант удобен для API, которые ожидают JSON, но не подходит для бинарных файлов без дополнительной логики.
- Код остаётся компактным и хорошо читаемым для типовых форм без upload-полей.
Для JSON файлы не сериализуются — для загрузки файлов оставляйте FormData или отдельный fetch с Blob. Отмена долгой отправки — AbortController; разбор ответа — в асинхронном программировании.
Практические правила
- Дублируйте критичные правила на сервере.
- Сообщения — рядом с полем, связь
label+id, для скринридеров —aria-invalid="true"иaria-describedbyна блок ошибки. - Не валидируйте скрытые поля (
display: none) так же, как видимые — пользователь не может исправить. - Для масок (телефон) —
inputmode,patternили маска в JS; регулярные выражения — дляpattern, не для всей логики формы.
Типовой сценарий — регистрация с API
Регистрация с API — это процесс создания новой учетной записи пользователя в веб-приложении, который выполняется путем отправки данных, введенных пользователем в регистрационную форму, на серверный API с использованием асинхронных HTTP-запросов, и который включает в себя как клиентскую валидацию для мгновенной обратной связи, так и серверную валидацию для обеспечения безопасности и целостности данных. При регистрации с API клиентская часть приложения собирает данные из формы, такие как имя пользователя, электронная почта и пароль, выполняет первичную валидацию, включая проверку обязательности полей, соответствие форматам и минимальную сложность пароля, и отправляет эти данные на серверный эндпоинт с использованием Fetch API и объекта FormData или JSON. Асинхронная проверка, такая как проверка доступности логина или электронной почты, может быть выполнена до отправки основной формы для улучшения пользовательского опыта, позволяя пользователю узнать о занятости имени или почты до того, как он заполнит все остальные поля и отправит форму. После успешной регистрации сервер обычно возвращает ответ с данными о созданном пользователе и токеном сессии или аутентификации, и клиентская часть приложения перенаправляет пользователя на защищенную страницу или показывает сообщение об успешной регистрации, а также сохраняет полученные токены для последующей аутентификации запросов. Важным аспектом регистрации с API является обработка различных сценариев ошибок, включая ошибки сети, серверные ошибки, конфликты данных, например, занятый логин или почта, а также ошибки валидации, которые должны быть корректно отображены пользователю с понятными и конкретными сообщениями, чтобы пользователь мог исправить ошибки и повторить попытку. Современные практики регистрации с API также включают использование механизмов защиты, таких как CAPTCHA для предотвращения автоматической регистрации, ограничение частоты запросов для предотвращения атак перебора, и шифрование паролей на стороне клиента и сервера для обеспечения конфиденциальности данных.
Практический поток в продакшене обычно строят так:
- Пользователь вводит данные, браузер проверяет базовые правила (
required,type,minlength). - На
submitскрипт запускаетform.checkValidity(). - При локальной ошибке интерфейс показывает подсказки у полей и фокус переводится на первое проблемное поле.
- При локальном успехе код отправляет
FormDataчерезfetch. - Сервер выполняет бизнес-валидацию (уникальность email, политика пароля, антиспам) и возвращает ответ.
Такой конвейер объединяет быстрый UX на клиенте и надёжную проверку на сервере. Разделение ролей особенно важно в формах оплаты и регистрации. Для транспортного слоя используйте материалы по HTTP и обработке API-ошибок. В React те же идеи — controlled inputs и onSubmit — см. галерею форм и валидации и 272.
Частые ошибки и исправления
| Ошибка | Что происходит | Как исправить |
|---|---|---|
Включён novalidate, но нет своего UI ошибок | пользователь нажимает submit и не видит причину блокировки | показывать сообщения рядом с полем и в общем блоке формы |
Сообщение в setCustomValidity не очищается | поле остаётся "красным" после исправления | сбрасывать setCustomValidity('') на каждом релевантном input |
| Валидация только в браузере | в API попадают некорректные или вредоносные данные | дублировать правила на бэкенде и логировать причины отказов |
| Ошибки идут только цветом рамки | часть пользователей не понимает проблему | добавить текст ошибки и aria-describedby для доступности |
Краткий итог
Constraint Validation API связывает HTML-атрибуты и скрипт — validity, checkValidity, setCustomValidity, reportValidity. Начните с нативных атрибутов, добавьте JS для парных полей и серверных правил, оформите ошибки через CSS и доступную разметку.