Открыть меню    

Формы HTML5: JavaScript

веб-формы HTML5 и JavaScript В последней статье о веб-формах HTML5 мы рассмотрим интеграцию JavaScript и Constraint Validation API. Напомню, что две предыдущие были посвящены основам CSS и разметки HTML. Ознакомиться с ними можно, пройдя по ссылкам:

  1. Формы HTML5: Разметка, modernizr
  2. Формы HTML5: CSS

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().

Также оба метода будут:

  1. устанавливают полям объект .validity, результатом чего является возможность проводить более полный анализ ошибок, и
  2. запускают событие 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).

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

Добавить комментарий