Встроенная валидация форм
Браузер умеет проверять форму сам — без единой строчки JavaScript. Разберём, как им управлять.
Встроенная (constraint) валидация — это набор HTML-атрибутов (
required,pattern,min/maxи др.), по которым браузер до отправки проверяет поля и сам показывает сообщения об ошибках.
Раньше «проверить форму на клиенте» означало писать JavaScript: ловить событие, бегать по полям, рисовать ошибки. Современный HTML делает большую часть этого декларативно. Вы описываете правила атрибутами — браузер берёт на себя проверку, блокировку отправки и подсказки пользователю.
Зачем это знать на практике
Встроенная валидация — первая линия обороны и удобства:
- Мгновенная обратная связь. Пользователь узнаёт об ошибке сразу, не дожидаясь ответа сервера.
- Меньше кода. Простые правила («обязательно», «не короче 8 символов», «похоже на e-mail») задаются атрибутами, без скриптов.
- Доступность из коробки. Сообщения браузера озвучиваются скринридерами, привязка к полю корректна.
Важно: клиентская проверка — для удобства, а не для безопасности. Её легко обойти. Сервер обязан проверять данные ещё раз — это правило без исключений.
Базовые атрибуты ограничений
Каждый атрибут описывает одно правило. Их комбинируют на одном поле.
| Атрибут | Что проверяет |
required | Поле не должно быть пустым. |
minlength / maxlength | Минимум/максимум символов в тексте. |
min / max | Границы для числа или даты. |
pattern | Значение должно совпасть с регулярным выражением. |
type="email" / type="url" | Встроенная проверка формата адреса/ссылки. |
<form>
<label>Имя
<input type="text" name="name" required minlength="2" maxlength="40">
</label>
<label>Возраст
<input type="number" name="age" required min="14" max="120">
</label>
<label>Почта
<input type="email" name="email" required>
</label>
<button>Отправить</button>
</form>
Если нажать «Отправить» с пустым обязательным полем, браузер не даст форме уйти и покажет всплывающую подсказку у первого проблемного поля. Числовое поле проверит границы, e-mail — формат.
pattern: проверка регулярным выражением
Атрибут pattern задаёт регулярное выражение, которому должно целиком соответствовать значение (якоря по краям подразумеваются автоматически). Это способ описать «свой формат»: индекс, артикул, логин.
<label>Индекс (6 цифр)
<input type="text" name="zip" pattern="[0-9]{6}"
title="Шесть цифр, например 101000">
</label>
<label>Логин (латиница, цифры, _ )
<input type="text" name="login" pattern="[a-zA-Z0-9_]{3,16}">
</label>
Два совета по pattern. Первое: добавляйте title — его текст браузер покажет как подсказку, что именно ждут. Второе: pattern не делает поле обязательным. Пустое значение проходит проверку шаблоном — чтобы запретить пустоту, добавьте required.
Псевдоклассы :valid и :invalid
Состояние проверки доступно в CSS — можно подсветить корректные и ошибочные поля без JavaScript:
input:invalid {
border-color: #d33;
}
input:valid {
border-color: #2a2;
}
/* подсветим только после взаимодействия */
input:user-invalid {
background: #fff5f5;
}
Тонкость UX: :invalid срабатывает сразу, даже на пустом обязательном поле, которое пользователь ещё не трогал — красить его красным с порога неприятно. Псевдокласс :user-invalid (и его «родственник» — поведение по событию) применяется только после того, как пользователь повзаимодействовал с полем или попытался отправить форму. Это и есть «правильный момент» для показа ошибки.
Отключение проверки: novalidate и formnovalidate
Иногда встроенная проверка мешает: на черновике формы, на кнопке «Сохранить как черновик», при собственной JS-валидации. Её выключают:
<!-- выключить проверку для всей формы -->
<form novalidate>
...
<!-- ...но эта кнопка отправит без проверки точечно -->
<button type="submit" formnovalidate>Сохранить черновик</button>
<button type="submit">Опубликовать</button>
</form>
novalidate на <form> отключает проверку целиком; formnovalidate на конкретной кнопке — только при отправке этой кнопкой. Удобно для сценария «сохранить наполовину заполненное».
Сочетание атрибутов и читаемые сообщения
Сила встроенной валидации — в комбинировании простых правил на одном поле. Они работают как логическое «И»: значение должно удовлетворять всем ограничениям сразу. Поле пароля «обязательно, от 8 до 64 символов, хотя бы одна цифра» собирается так:
<label>Пароль
<input type="password" name="pwd"
required minlength="8" maxlength="64"
pattern=".*[0-9].*"
title="Минимум 8 символов и хотя бы одна цифра">
</label>
Здесь required запрещает пустоту, minlength/maxlength ограничивают длину, а pattern требует наличия цифры. Если нарушено несколько правил, браузер сообщит о первом нарушенном. Текст в title особенно важен для pattern: стандартное сообщение «Введите значение в требуемом формате» бесполезно, а ваше — объясняет, чего именно не хватает. Хорошая подсказка снижает раздражение сильнее, чем любое визуальное оформление ошибки.
Как это работает под капотом
За каждым полем стоит объект состояния валидности (ValidityState) с булевыми флагами: valueMissing (нарушен required), tooShort/tooLong, rangeUnderflow/rangeOverflow, patternMismatch, typeMismatch (плохой e-mail/url). Когда пользователь жмёт submit, браузер опрашивает все поля; если хоть одно невалидно, отправка отменяется, фокус и подсказка переходят к первому проблемному.
Текст подсказки по умолчанию формирует сам браузер (и переводит на язык интерфейса). Его можно заменить из JavaScript методом setCustomValidity:
<label>Пароль
<input id="pwd" type="password" minlength="8" required>
</label>
<!-- сам код навешивается в JS-файле, без inline-обработчиков -->
В скрипте: если значение не подходит — input.setCustomValidity("Минимум 8 символов"); когда исправили — input.setCustomValidity("") (пустая строка снимает ошибку). Непустое сообщение делает поле невалидным и показывается вместо стандартного. Это нужно нечасто — для нетривиальных правил вроде «пароли должны совпадать».
Частые ошибки
- Считать клиентскую проверку защитой. Её обходят за секунды (DevTools, прямой запрос). Сервер обязан валидировать данные заново.
- Забывать
requiredрядом сpattern. Пустое поле проходитpattern. Если пустота недопустима — нужен иrequired. - Красить
:invalidс самого начала. Пустые обязательные поля сразу краснеют и пугают. Используйте:user-invalidили показывайте ошибку после первого взаимодействия. - Путать
maxlengthиmax.maxlength— про число символов в тексте;max— про значение числа или даты. - Не снимать
setCustomValidity. Если забыть вызвать его с пустой строкой после исправления, поле останется «вечно невалидным».
Итоги
- Браузер проверяет форму до отправки по атрибутам
required,minlength/maxlength,min/max,pattern, типамemail/url. patternсверяет значение с регулярным выражением целиком; добавляйтеtitleдля подсказки иrequired, если пустота недопустима.- Псевдоклассы
:valid/:invalidдают подсветку из CSS;:user-invalidсрабатывает только после взаимодействия — это правильный момент для ошибки. novalidateна форме иformnovalidateна кнопке отключают встроенную проверку (например, для черновика).setCustomValidity("текст")задаёт своё сообщение,setCustomValidity("")снимает ошибку. Клиентская проверка — не замена серверной.