Формы в HTML5: JavaScript и API для принудительной валидации форм
Содержание цикла статей "Формы в HTML 5":
Часть 1 Формы в HTML5: Разметка;
Часть 2 Формы в HTML5: CSS;
Часть 3 Формы в HTML5: JavaScript и API для принудительной валидации форм.
В последней из трех статей на тему форм в HTML5 мы обсудим интеграцию JavaScript и constraint validation api. Если вы еще не готовы к этой теме, то вам стоит прочесть статьи Формы в HTML5: Разметка и Формы в HTML5: CSS чтобы вспомнить основы.
HTML5 позволяет нам применить валидацию форм на стороне клиента без использования JavaScript. Тем не менее, при использовании сложных форм ввода, нам понадобится усовершенствовать встроенную валидацию по следующим причинам:
- не все браузеры поддерживают абсолютно все типы полей ввода и селекторов CSS;
- сообщения об ошибках используют общий текст («пожалуйста, заполните это поле») и их сложно типизировать;
- селекторы :invalid и :required применяются при загрузке страницы до того, как пользователь начнет взаимодействовать с формой.
Используя JavaScript и Constraint Validation API, мы сможем расширить наши возможности. Имейте в виду, что код получится не сильно прозрачным, если мы будем учитывать большое число браузеров и типов полей ввода.
Перехватываем данные из формы
Первая часть валидации, до HTML5, присоединяет к форме обработчик submit, который будет проверять поля, отображать ошибки и предотвращать событие подтверждения.
На уровне HTML5 браузер сначала выполнит свою собственную проверку - событие submit не произойдет, пока форма не станет действительна.
Затем, если вы захотите исполнить что-то более сложное, к примеру, отобразить свои сообщения об ошибках, сравнить или заполнить поля автоматически, вам придется выключить встроенную валидацию установив свойство noValidate в значение true:
var form = document.getElementById("myform");
form.noValidate = true;
// обработчик выполняет проверку по событию onsubmit
// используем для поддержания кроссбраузерности
form.onsubmit = validateForm;
Конечно же, в этом случае вам придется запрограммировать проверку полей самостоятельно, но вы также можете использовать и встроенную валидацию браузера, что мы рассмотрим далее.
Свойство поля .willValidate
Каждое поле формы имеет свойство .willValidate. Оно возвращает следующие значения:
- true когда браузер для проверки будет использовать встроенную валидацию;
- false когда браузер не будет проверять поле;
- undefined когда браузер не поддерживает встроенную валидацию форм HTML5, к примеру в IE8.
Так как выше мы выключили встроенную валидацию, каждое поле вернет значение false.
Создадим собственный обработчик validateForm, который будет проверять, включена ли проверка браузеров у каждого поля:
function validateForm(event) {
// fetch cross-browser event object and form node
event = (event ? event : window.event);
var
form = (event.target ? event.target : event.srcElement),
f, field, formvalid = true;
// перебираем поля
for (f = 0; f < form.elements; f++) {
// получаем поле
field = form.elements[f];
// игнорируем кнопки, наборы полей, и т.д.
if (field.nodeName !== "INPUT" && field.nodeName !== "TEXTAREA" &
& field.nodeName !== "SELECT") continue;
Операция повторяется на всех полях elements формы и проверяет, являются ли элементы полями ввода, а не кнопками или группой полей. Обратите особое внимание на следующую строку:
// встроенная валидация браузера включена?
if (typeof field.willValidate !== "undefined") {
// в случае если включена
}
else {
// в случае когда НЕ включена
}
Оба значения false и undefined - значения ложные, потому вы не сможете их проверить просто с помощью field.willValidate! И еще мы знаем о том, что код из первого блока будет иметь значение, только когда работает встроенная валидация. Однако…
Поддерживает ли браузер тип полей ввода?
Если вы помните, в первой части говорилось о том, что фолбэк в случае неподдерживаемых типов полей ввода - поле text. К примеру:
<input type="date" name="dob" />
не имеет встроенной поддержки в Firefox 29 или IE11. Эти браузеры (фактически) отобразят следующий код:
<input type="text" name="dob" />
Но оба браузера поддерживают встроенную валидацию для типа поля text и потому field.willValidate не вернет значение undefined!
Поэтому сначала мы должны убедиться в том, что атрибуты type соответствуют свойствам объекта .type — и если это не так, мы должны реализовать проверку для фолбэка, к примеру, такую:
// работает встроенная валидация
if (field.nodeName === "INPUT" && field.type !== field.getAttribute("type")) {
// поле ввода не поддерживается! Используем валидацию на JavaScript
}
Метод .checkValidity()
В случае, когда встроенная валидация возможна, для проверки полей может быть использован метод .checkValidity(). Он вернет значение true, если проблем не обнаружит, или false, в остальных случаях.
Похожий метод .reportValidity(), который возвращает текущее состояние поля без его повторной проверки. Но он не поддерживается всеми браузерами, потому не столь полезен, как предыдущий.
Оба метода делают следующее:
- возвращают свойство объектов .validity, которое поможет детально рассмотреть ошибки;
- вызывают событие invalid при ошибке валидации. Это можно использовать для отображения ошибок, смены цвета полей и так далее. Обратите внимание, что нет соответствующего события valid, поэтому не забудьте сбросить стили ошибок и сообщений, при необходимости.
Объект Field .validity
Объект .validity имеет следующие свойства:
- .valid – возвращает true, если поле без ошибок и false в противном случае;
- .valueMissing – возвращает true, если значение в поле отсутствует, но оно требуется;
- .typeMismatch – возвращает true, если значение не соответствует синтаксису, к примеру, не корректно введен адрес электронной почты;
- .patternMismatch – возвращает true, если значение не соответствует выражению в атрибуте pattern;
- .tooLong – возвращает true, если значение превышает допустимую длину maxlength;.
- .tooShort – возвращает true, если значение меньше допустимого минимума minlength;.
- .rangeUnderFlow – возвращает true, если значение меньше допустимого min;
- .rangeOverflow – возвращает true, если значение больше допустимого max;
- .stepMismatch – возвращает true, если значение введено с недопустимым шагом step;
- .badInput – возвращает true, если запись не может быть преобразована в значение;
- .customError – возвращает true, если поле имеет набор ошибок пользователя.
Не все эти свойства поддерживаются всеми браузерами, потому сильно не увлекайтесь. В большинстве случае достаточно возвращаемых значений от .valid или .checkValidity() для отображения сообщений об ошибках.
Поддержка объекта .validity устаревшими браузерами
Эмулировать объект .validity в устаревших браузерах можно вручную, к примеру:
// встроенная валидация недоступна
field.validity = field.validity || {};
// результат функции валидации
field.validity.valid = LegacyValidation(field);
Этим методом .validity.valid может быть протестирован в любом браузере.
Метод.setCustomValidity()
Методу .setCustomValidity() можно передать:
- пустую строку. Это подразумевает, что поле ввода содержит корректные данные, потому и .checkValidity() и .validity.valid вернет значение true;
- строку, содержащую сообщение об ошибке, которое будет показано во всплывающем сообщении (если используется).
Сообщение пометит поле как поле с ошибочными данными и потому .checkValidity() и .validity.valid возвратят значение false и произойдет событие invalid.
Обратите внимание, что вы также можете проверить текущее сообщение, используя свойство поля .validationMessage.
Подведем итоги
Теперь, у нас есть простая, кроссбраузерная система проверки формы:
var form = document.getElementById("myform");
form.noValidate = true;
// обработчик выполняет проверку по событию onsubmit
// используем для поддержания кроссбраузерности
form.onsubmit = validateForm;
function validateForm(event) {
// проверка на кроссбраузерность
event = (event ? event : window.event);
var
form = (event.target ? event.target : event.srcElement),
f, field, formvalid = true;
// перебираем все поля
for (f = 0; f < form.elements; f++) {
// считываем поле
field = form.elements[f];
// пропуская кнопки, наборы полей, и т.п.
if (field.nodeName !== "INPUT" && field.nodeName !== "TEXTAREA" && field.nodeName !== "SELECT") continue;
// проверяем, возможна ли встроенная валидация?
if (typeof field.willValidate !== "undefined") {
// возможна
if (field.nodeName === "INPUT" && field.type !== field.getAttribute("type")) {
// поле ввода не поддерживается! Используем валидацию JavaScript
field.setCustomValidity(LegacyValidation(field) ? "" : "error");
// выполняем валидацию
field.checkValidity();
}
else {
// не возможна встроенная проверка
field.validity = field.validity || {};
// результат функции проверки
field.validity.valid = LegacyValidation(field);
// если требуются события "invalid", включайте их здесь
}
if (field.validity.valid) {
// убираем сообщения об ошибках
}
else {
// стилизуем сообщения, показываем ошибки, и т.д.
// форма неверна
formvalid = false;
}
}
// если валидация не завершилась успешно, отменяем подтверждение формы
if (!formvalid) {
if (event.preventDefault) event.preventDefault();
}
return formvalid;
}
// базовая валидация
function LegacyValidation(field) {
var
valid = true,
val = field.value,
type = field.getAttribute("type"),
chkbox = (type === "checkbox" || type === "radio"),
required = field.getAttribute("required"),
minlength = field.getAttribute("minlength"),
maxlength = field.getAttribute("maxlength"),
pattern = field.getAttribute("pattern");
// недоступные поля не проверяются
if (field.disabled) return valid;
// требуется значение?
valid = valid && (!required ||
(chkbox && field.checked) ||
(!chkbox && val !== "")
);
// установлены minlength или maxlength?
valid = valid && (chkbox || (
(!minlength || val.length >= minlength) &&
(!maxlength || val.length <= maxlength)
));
// тест паттерна
if (valid && pattern) {
pattern = new RegExp(pattern);
valid = pattern.test(val);
}
return valid;
}
Методу LegacyValidation преднамеренно оставили самое простое; он проверяет регулярные выражения required, minlength, maxlength и pattern, и вам нужно добавить свой код для проверок email, URL, дат, чисел, диапазонов.
Это приводит к вопросу: если вы пишете код валидации для устаревших браузеров, зачем обращаться за помощью к встроенным API браузера? Отличный вопрос! Код, показанный выше, написан для поддержки валидации форм всеми браузерами, начиная с IE6 и выше. Это ведь не всегда нужно…
- Вы можете не использовать код JavaScript для проверки простых форм. Пользователи устаревших браузеров в случае фолбэка попадут на валидацию со стороны сервера. И она должна быть всегда предусмотрена;
- Если вам потребуется использовать более сложные формы, но нужно поддерживать только новейшие браузеры (Internet Explorer 10 +), вы можете удалить весь код проверки для устаревших браузеров. Дополнительно вам может потребоваться проверка полей дат, которые в настоящее время не поддерживаются в Firefox и IE;
- Даже если вам требуется код для проверки таких полей, как email, numbers и т.п. в IE9 и более ранних версиях, старайтесь не усложнять его и убирайте, как только он перестанет быть необходим. Сейчас требуется немного грязный код, но ситуация со временем улучшится.
Помните, что в HTML5 необходимо всегда использовать правильный тип поля. Браузер обеспечит использование встроенных элементов ввода и применит валидацию на стороне клиента, даже если Javascript будет отключен.