Формы 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).
Комментарии к статье