Перейти к основному содержимому

Валидация форм в JavaScript

Разработчику Архитектору

Валидация — это процесс проверки данных на соответствие заданным правилам, требованиям и ограничениям, который выполняется для обеспечения целостности, безопасности и корректности информации, поступающей в приложение, прежде чем она будет использована в бизнес-логике, сохранена в базе данных или передана другим системам. Валидация охватывает широкий спектр проверок, включая обязательность полей, соответствие типам данных, форматам, длине строк, допустимому диапазону значений, уникальность, а также более сложные бизнес-правила, такие как взаимосвязь между полями, проверка возраста или соответствие определенным паттернам, например, электронной почте или номеру телефона. Валидация может выполняться на разных уровнях приложения, включая клиентскую часть для мгновенной обратной связи с пользователем, серверную часть для обеспечения безопасности и целостности данных, а также на уровне базы данных для гарантии структурной согласованности. Эффективная валидация является критически важным аспектом разработки программного обеспечения, поскольку она предотвращает множество проблем, включая ошибки выполнения, повреждение данных, атаки на безопасность, такие как инъекции SQL и межсайтовый скриптинг, а также улучшает пользовательский опыт, предоставляя четкие и понятные сообщения об ошибках при неправильном вводе. В современной веб-разработке валидация часто реализуется с использованием как встроенных браузерных механизмов, так и пользовательских функций и библиотек, причем лучшие практики рекомендуют использовать валидацию на обоих уровнях — клиентском для удобства пользователя и серверном для безопасности и надежности.

Валидация форм — это специализированный процесс проверки данных, введенных пользователем в поля веб-формы, который выполняется для обеспечения того, что все обязательные поля заполнены, данные соответствуют ожидаемым форматам и правилам, и форма может быть безопасно отправлена на сервер для дальнейшей обработки. Валидация форм в браузере может быть реализована с использованием встроенных HTML-атрибутов, таких как required для обязательных полей, type для указания типа данных, minlength и maxlength для ограничения длины, pattern для задания регулярного выражения, min и max для числовых диапазонов, а также с использованием JavaScript для более сложной кастомной логики проверки. Современные браузеры предоставляют встроенную валидацию форм через API проверки достоверности, который позволяет разработчику получать доступ к состоянию валидации каждого поля через объект validity и свойство valid, а также управлять отображением стандартных сообщений об ошибках через метод setCustomValidity. Валидация форм на клиентской стороне обеспечивает мгновенную обратную связь пользователю, подсвечивая некорректные поля и показывая понятные сообщения об ошибках без необходимости отправки данных на сервер, что значительно улучшает пользовательский опыт и снижает нагрузку на сервер. Однако клиентская валидация никогда не должна рассматриваться как замена серверной валидации, поскольку она может быть обойдена злоумышленником или отключена в браузере, поэтому критически важно дублировать все проверки на серверной стороне для обеспечения безопасности и целостности данных.

Два уровня проверки

Браузер умеет проверять поля до отправки на сервер:

  1. HTMLrequired, minlength, type="email", type="date", pattern, min / max (см. типы <input> и справочник HTML).
  2. 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, passwordvalue, minLength, maxLength, placeholder, pattern, selectionStart / selectionEnd, select()
number, rangevalue, min, max, step, stepUp(n?), stepDown(n?)
date, time, datetime-local, month, weekvalue в формате ISO (2026-05-30, 14:30, …), min, max, step
colorvalue как #RRGGBB (например #ff0000)
checkboxchecked, defaultChecked, indeterminate (промежуточное состояние "частично выбрано")
radiochecked; группа полей с одним name — через document.querySelectorAll('input[name="gender"]')
filefiles (FileList), multiple, accept; у каждого File: name, size, type, lastModified
hiddenтолько value
submit, reset, button, imagevalue; у 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
textareavalue, rows, cols, wrap (soft / hard / off), spellcheck; Enter по умолчанию — новая строка, не submit
selectvalue — значение выбранного <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 синхронный. Проверка "логин свободен на сервере" делается так:

  1. На submitpreventDefault.
  2. fetch на API.
  3. При конфликте — field.setCustomValidity('Логин занят') и field.reportValidity().
  4. При успехе — 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 для предотвращения автоматической регистрации, ограничение частоты запросов для предотвращения атак перебора, и шифрование паролей на стороне клиента и сервера для обеспечения конфиденциальности данных.

Практический поток в продакшене обычно строят так:

  1. Пользователь вводит данные, браузер проверяет базовые правила (required, type, minlength).
  2. На submit скрипт запускает form.checkValidity().
  3. При локальной ошибке интерфейс показывает подсказки у полей и фокус переводится на первое проблемное поле.
  4. При локальном успехе код отправляет FormData через fetch.
  5. Сервер выполняет бизнес-валидацию (уникальность 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 и доступную разметку.