Валидация формы в JavaScript
Валидация формы - это проверка введённых данных перед их обработкой или отправкой. Например, обязательное поле не должно быть пустым, адрес электронной почты должен иметь подходящий формат, а два введённых пароля должны совпадать.
Часть правил можно задать прямо в HTML. JavaScript дополняет их, когда нужно запустить проверку из кода, узнать причину ошибки или проверить условие, которое нельзя выразить одним HTML-атрибутом.
- Встроенная и пользовательская валидация
- Ограничения в HTML
- Проверка при отправке формы
- checkValidity() и reportValidity()
- Свойство validity
- Сообщение validationMessage
- Собственное правило через setCustomValidity()
- Пример проверки формы
- Когда нужен novalidate
- Проверка на сервере
- Итого
Встроенная и пользовательская валидация
Для проверки формы обычно используют два уровня:
- HTML-ограничения проверяют стандартные случаи: пустое поле, формат электронной почты, длину строки, диапазон числа;
- JavaScript проверяет правила приложения: совпадение паролей, допустимость сочетания полей или другие условия.
Не стоит полностью переносить в JavaScript то, что браузер уже умеет проверять через HTML. Атрибут required понятнее ручной проверки пустого значения, а type=“email” сразу сообщает браузеру назначение поля.
Ограничения в HTML
Создадим форму регистрации:
<form class="registration-form">
<label for="username">Имя</label>
<input
id="username"
name="username"
type="text"
minlength="2"
maxlength="30"
required
>
<label for="email">Электронная почта</label>
<input id="email" name="email" type="email" required>
<label for="age">Возраст</label>
<input id="age" name="age" type="number" min="14" max="120">
<label for="password">Пароль</label>
<input
id="password"
name="password"
type="password"
minlength="8"
required
>
<label for="password-confirmation">Повторите пароль</label>
<input
id="password-confirmation"
name="passwordConfirmation"
type="password"
required
>
<label>
<input name="agreement" type="checkbox" required>
Я принимаю условия
</label>
<button type="submit">Зарегистрироваться</button>
</form>
<p class="registration-status" aria-live="polite"></p>
const registrationForm = document.querySelector('.registration-form');
const registrationStatus = document.querySelector('.registration-status');
В примере браузер уже может проверить большую часть данных:
| Атрибут | Что проверяет |
|---|---|
required | Значение обязательно или checkbox должен быть отмечен |
type=“email” | Строка имеет допустимый для браузера формат электронной почты |
minlength и maxlength | Длина текста находится в заданных пределах |
min и max | Число или дата входят в допустимый диапазон |
step | Значение соответствует заданному шагу |
pattern | Текст соответствует регулярному выражению |
Если поле необязательное и осталось пустым, ограничения формата обычно не создают ошибку. Например, пустое поле возраста допустимо, потому что у него нет required.
Атрибут required считает строку из пробелов заполненным значением. Если в конкретном поле такой ввод недопустим, его дополнительно проверяют в JavaScript с помощью trim().
Проверка при отправке формы
Браузер автоматически проверяет ограничения перед событием submit. Если форма невалидна, он отменяет отправку, показывает сообщение и переводит фокус к проблемному полю. Обработчик submit в этом случае не запускается.
Когда все встроенные ограничения выполнены, запускается обработчик submit. В нём можно проверить дополнительные правила приложения, получить данные и отправить их.
Не переносите проверку только в обработчикclickна кнопке. Работайте с событиемsubmitформы, чтобы учесть отправку мышью, с клавиатуры и стандартные HTML-ограничения.
checkValidity() и reportValidity()
Иногда форму нужно проверить из JavaScript до отправки. Метод checkValidity() возвращает true, если все поля прошли проверку, и false, если найдена ошибка:
const isFormValid = registrationForm.checkValidity();
console.log(isFormValid); // true или false
Метод не показывает стандартные сообщения браузера. Если нужно не только получить результат, но и показать пользователю ошибку, используют reportValidity():
const isFormValid = registrationForm.reportValidity();
console.log(isFormValid); // true или false
reportValidity() проверит форму, покажет стандартное сообщение у первого неподходящего поля и вернёт результат.
Оба метода доступны и отдельным элементам:
const emailInput = registrationForm.elements.email;
console.log(emailInput.checkValidity());
emailInput.reportValidity();
Для программной отправки с обычной проверкой используют requestSubmit(). Метод form.submit() отправляет форму напрямую и обходит встроенную валидацию и событие submit.
Свойство validity
Свойство validity содержит объект ValidityState. Его свойства помогают определить, какое именно правило не выполнено.
const emailInput = registrationForm.elements.email;
console.log(emailInput.validity.valid);
console.log(emailInput.validity.valueMissing);
console.log(emailInput.validity.typeMismatch);
Основные свойства ValidityState:
| Свойство | Причина ошибки |
|---|---|
valueMissing | Обязательное поле не заполнено |
typeMismatch | Значение не соответствует типу, например формату email |
tooShort или tooLong | Нарушено ограничение длины |
rangeUnderflow или rangeOverflow | Число или дата выходят за пределы min и max |
stepMismatch | Значение не соответствует step |
patternMismatch | Значение не соответствует pattern |
customError | Ошибка задана через setCustomValidity() |
valid | Все применимые проверки пройдены |
Каждое свойство, кроме valid, становится равным true, когда обнаружена соответствующая проблема. У одного поля может быть несколько причин невалидности, поэтому порядок показа сообщений определяет ваш код или браузер.
Сообщение validationMessage
Свойство validationMessage содержит готовый текст ошибки. Для стандартных ограничений браузер формирует и переводит его самостоятельно:
const message = emailInput.validationMessage;
console.log(message);
Если поле валидно, свойство возвращает пустую строку. Точный текст зависит от браузера, языка системы и нарушенного правила. Поэтому не стоит сравнивать validationMessage с заранее записанной фразой. Причину ошибки проверяют через validity.
Собственное правило через setCustomValidity()
HTML не умеет сравнивать два поля. Проверим через JavaScript, совпадают ли пароль и его подтверждение:
const passwordInput = registrationForm.elements.password;
const confirmationInput = registrationForm.elements.passwordConfirmation;
function validatePasswordConfirmation() {
const hasConfirmation = confirmationInput.value !== '';
const passwordsDiffer = (
hasConfirmation && confirmationInput.value !== passwordInput.value
);
confirmationInput.setCustomValidity(
passwordsDiffer ? 'Пароли не совпадают' : '',
);
}
passwordInput.addEventListener('input', validatePasswordConfirmation);
confirmationInput.addEventListener('input', validatePasswordConfirmation);
Если пароли различаются, setCustomValidity() сохраняет сообщение и делает поле невалидным. Когда ошибка исправлена, в метод обязательно передаётся пустая строка. Иначе customError останется равным true, и форма продолжит считаться невалидной.
Пустое подтверждение в функции не получает собственную ошибку, потому что этот случай уже проверяет атрибут required.
Проверка запускается при вводе в каждое из двух полей. Это важно: если пользователь изменит исходный пароль после заполнения подтверждения, состояние сразу обновится.
Пример проверки формы
Объединим пользовательское правило с обработкой отправки:
registrationForm.addEventListener('submit', function (event) {
validatePasswordConfirmation();
if (!registrationForm.reportValidity()) {
event.preventDefault();
return;
}
event.preventDefault();
registrationStatus.textContent = 'Данные можно отправлять';
});
Сначала функция обновляет состояние подтверждения пароля. Затем reportValidity() повторно проверяет форму и показывает ошибку, если собственное правило не выполнено.
Браузер выполняет первоначальную проверку ещё до submit. Поэтому при пустом обязательном поле обработчик вообще не запустится. Повторный вызов нужен для правила, которое код обновляет непосредственно внутри обработчика.
В примере стандартная отправка отменяется и после успешной проверки, потому что серверного обработчика нет. Если форма должна отправляться обычным способом, последний event.preventDefault() не нужен. При отправке через fetch() на его месте выполняют сетевой запрос.
Когда нужен novalidate
Атрибут novalidate у формы отключает автоматический показ сообщений и блокировку отправки браузером:
<form class="registration-form" novalidate>
<!-- поля формы -->
</form>
Он нужен, если интерфейс полностью самостоятельно показывает ошибки рядом с полями. В таком случае обработчик submit должен вызвать checkValidity(), вывести сообщения, перевести фокус к первому ошибочному полю и остановить обработку невалидных данных.
Сам атрибут не делает форму валидной и не отключает методы проверки. checkValidity(), свойства validity и validationMessage продолжат работать.
Не добавляйте novalidate, если собственный интерфейс ошибок не реализован. Иначе пользователь сможет запустить отправку формы, не получив понятного сообщения о проблеме.
Проверка на сервере
Валидация в браузере помогает пользователю исправить данные до отправки, но не является защитой. HTML-атрибуты и JavaScript можно изменить или полностью обойти.
Сервер должен заново проверять обязательные поля, формат и длину значений, допустимые диапазоны, права пользователя и правила приложения. Например, только сервер может надёжно определить, свободен ли адрес электронной почты и разрешено ли текущее действие.
Если сервер отклонил данные, интерфейс должен показать понятное сообщение и сохранить введённые значения, чтобы пользователь мог исправить ошибку.
Итого
Стандартные правила задают через required, type, min, max, minlength, maxlength, step и pattern. Браузер проверяет их перед событием submit.
checkValidity() возвращает результат проверки, reportValidity() дополнительно показывает стандартное сообщение, а validity помогает определить причину ошибки. Пользовательские правила задают через setCustomValidity(), обязательно очищая сообщение пустой строкой после исправления значения. Любые данные необходимо повторно проверять на сервере.