Формы HTML5: JavaScript
В последней статье о веб-формах HTML5 мы рассмотрим интеграцию JavaScript и Constraint Validation API. Напомню, что две предыдущие были посвящены основам CSS и разметки HTML. Ознакомиться с ними можно, пройдя по ссылкам:
HTML5 позволяет производить валидацию форм на клиентской стороне без использования JavaScript. Для сложных форм, однако, встроенная валидация нуждается в определенных преобразованиях (усилении). Это связано с тем, что:
- браузеры поддерживают не все типы HTML5 для полей ввода, и не все селекторы CSS
- всплывающие блоки с ошибками содержат типовой текст («пожалуйста, заполните это поле»/«please fill out this field»); правка стиля для таких блоков несет в себе определенные трудности
- стили
:invalidи:requiredприменяются после загрузки страницы, еще до того как пользователь начнет работать с формой
Облегчить работу пользователя с формами помогут JavaScript и Constraint Validation API. В статье мы попытаемся реализовать поддержку для как можно большего количества браузеров и типов полей ввода. Поэтому не стоит удивляться, если код получится не совсем прозрачным.
Перехват форм
До появления HTML5 валидация на клиентской стороне нуждалась в прикреплении к форме обработчика submit. Обработчик производил валидацию полей, показывал ошибки и предотвращал отправку данных формы.
В HTML5 браузер сперва выполняет свою собственную валидацию; событие submit запускается только после успешной валидации формы. Поэтому для того чтобы сделать что-то нестандартное, как то: отобразить свои ошибки, сравнение или автозаполнение полей – родную валидацию надо отключать, для этого свойству noValidate нужно задать значение true:
jQuery or Javascript
var form = document.getElementById("myform");
form.noValidate = true;
// устанавливаем обработчик, чтобы проверить форму
// onsubmit используется для обеспечения кроссбраузерности
form.onsubmit = validateForm;
То есть можно сделать вывод, что проверку полей на наличие ошибок придется осуществлять в коде, однако, как мы увидим позже, и встроенная валидация браузера все еще возможна.
Свойство поля .willValidate
Каждое поле ввода имеет свойство .willValidate. Оно возвращает:
trueкогда браузер будет использовать встроенную валидацию для полей вводаfalseвстроенная валидация браузером не производится илиundefinedподдержка родной HTML5 валидации в браузере не реализована (пример, IE8).
Так как выше мы отключили встроенную валидацию, каждое поле будет возвращать false. Давайте создадим обработчик validateForm, который будет проходить в цикле через все поля и проверять доступность у поля встроенной валидации:
jQuery or Javascript
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;
Цикл проходит через все поля формы посредством свойства формы elements (любой элемент формы form можно получить, используя свойство form.elements, ); при прохождении осуществляется проверка, являются ли они полями ввода или нет (например, кнопками, select, и т.д. ). Следующему блоку кода следует уделить особое внимание...
Javascript
// встроенная валидация браузера доступна?
//напомню: undefined поддержка родной HTML5 валидации в браузере не реализована
if (typeof field.willValidate !== "undefined") {
// встроенная валидация доступна
}
else {
// встроенная валидация недоступна
}
Теперь мы знаем, что код внутри первого блока будет задействован, только когда будет доступна встроенная валидация. Однако...
Поддерживает ли браузер тип полей ввода?
В первой статье говорилось, что вместо неподдерживаемых типов ввода используется тип text. Пример:
HTML
<input type="date" name="dob" />
Если тип не поддерживается, например, в firefox 29 и IE11. Браузеры выведут:
HTML
<input type="text" name="dob" />
Но, оба браузера поддерживают валидацию для типа text, поэтому field.willValidate не вернет undefined! Следовательно, нужно проверить, что атрибут type соответствует свойству .type. В случае несоответствия надо создать резервную валидацию. Пример:
Javascript
// встроенная валидация доступна
if (field.nodeName === "INPUT" && field.type !== field.getAttribute("type")) {
// поле ввода не поддерживается! Используем резервную на JavaScript
}
Метод поля .checkValidity()
Если встроенная браузерная валидация поддерживается, проверить поле можно методом .checkValidity(). В случае успешного прохождения валидации метод возвращает true, если же валидация не удалась, то – false.
Также есть метод .reportValidity(). Он возвращает текущее состояние без повторной проверки. Этот метод поддерживается не всеми браузерами и не столь эффективен, как метод .checkValidity().
Также оба метода будут:
- устанавливают полям объект
.validity, результатом чего является возможность проводить более полный анализ ошибок, и -
запускают событие
invalidпри неудачной валидации. Это можно использовать для показа ошибок, изменить цвета и т.д. Параллельного событияvalidне существует, поэтому при необходимости сообщения и стили для ошибок следует сбросить.
Объект поля .validity
Объект .validity имеет следующие свойства:
.valid– возвращаетtrue, если поле не содержит ошибок, иfalse- если содержит..valueMissing– возвращаетtrue, когда у поля естьrequired, а значение еще не было введено..typeMismatch– возвращаетtrueпри ошибочном синтаксисе значения (пример, неправильно форматированный адрес email).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 можно сэмулировать вручную:
Javascript
// встроенная валидация не поддерживается
field.validity = field.validity || {};
// результат функции валидации
field.validity.valid = LegacyValidation(field);
Благодаря чему .validity.valid можно протестировать во всех браузерах.
Метод поля .setCustomValidity()
Метод .setCustomValidity() может принимать:
- пустую строку. Поле становится валидным, поэтому
.checkValidity()и.validity.validвозвращаютtrue, или - строку, которая содержит сообщение об ошибке, будет выведена в блоке с ошибкой (если оно используется). Сообщение также отмечает поле как дефектное, поэтому
.checkValidity()и.validity.validвозвращаютfalseи запускается событиеinvalid.
Текущее сообщение также можно проверить с помощью свойства поля .validationMessage.
Мы сделали простую кроссбраузерную систему валидации формы:
Javascript
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 сознательно сделана простой. Она проверяет регулярное выражения pattern, required, minlength, maxlength; но чтобы проверить email, URL, даты, числа, ранг, понадобится дополнительный код.
Отсюда возникает вопрос: если пишется код валидации полей для устаревших(всех) браузеров, зачем использовать родные APIs браузера? Логично. Код выше необходим только в том случае, если требуется поддержка всех браузеров, начиная с IE6 и выше, когда нужно, чтобы сайт был одинаково удобен для всех пользователей вне зависимости от используемых ими браузеров. Необходимо это не всегда...
- для простых форм иногда можно обойтись без JavaScript кода. Что касается устаревших браузеров, то тут на помощь придет валидация со стороне сервера.
- для более сложных форм, но когда поддерживаются только новые браузеры (IE10+), весь код для устаревших браузеров можно удалить. Дополнительный JavaScript понадобится только в случае использования формами дат. В настоящий момент поддержка дат в Firefox и IE не реализована.
- даже если вы используете код для проверки полей типа email, цифр и т.д. в браузерах IE9 и старше, делайте его как можно проще. Когда поддержка браузера прекращается, код следует удалить.
Всегда используйте корректный HTML5 тип поля. В будущем в браузерах будет реализована поддержка встроенной валидации для элементов ввода, а валидация на стороне пользователя станет еще быстрее (даже при отключенном JavaScript).

Комментарии к статье