Валидация полей произвольных форм и контейнеров на jQuery

Валидация полей произвольных форм и контейнеров на jQuery

Вам когда-нибудь приходилось искать скрипты и плагины для валидации произвольных форм и контейнеров? Когда тэг "form" не используемая конструкция? И когда необходимо проверять корректность полей в любое время, а не по зашитым концептуальным механизмам? Можно долго спорить о необходимости соблюдать стандартны и поддерживать отправку данных через полную перезагрузку страницы. Тем не менее, сегодня, большинство web-приложений уже не возможно представить без поддержки javascript-a. Отключите javascript - и сервисы станут бесполезными, несмотря на то, что вы сможете зарегистрироваться, войти и пройтись по нескольким страницам.

Примечание: Речь идет о том, что тег form предназначен для отправки данных методом POST и требует перезагрузки страницы или фрейма. Тем не менее, чем дальше, тем реже используется этот метод, и тем чаще те же самые данные отправляются ajax-ом, которому в общем все равно, какие теги использовались перед формированием данных. 

Примечание: Конечно, существуют интернет-гиганты, которым в обязательном порядке необходимо поддерживать разные способы и режимы. Но, в их случае даже самый маленький процент подразумевает миллионы пользователей.

 

Рассмотрим существующие плагины для валидации форм на jQuery

Сноска: Название плагинов умышленно не приводится в статье, так как задачей является не критика конкретных приложений, а создание простого инструмента, который можно применить к произвольным формам.

Большинство существующих скриптов для валидации форм имеют несколько серьезных ограничений, которые далеко не всегда можно обойти простыми заглушкам или которые потребуют от вас длительной рутины.

  • Жесткая привязка к тэгу "form". Поэтому если у вас есть несколько десятков форм, построенных на div-ах или таблицах, и вы твердо решили использовать популярные решения, то готовьтесь потратить кучу времени на ручное добавление тегов и корректировку скриптов и стилей.  
  • Привязка к именам полей. Некоторые валидаторы привязываются к названиям полей, а это значит написание простыней javasript-а для каждого случая. Если для пары форм это не страшно, то для коллекции это уже означает длительную ручную генерацию кода.
  • Отсутствие валидации по требованию, без необходимости инициализации плагина. Вы не можете взять и один раз вручную проверить форму. Нужно правильно составить форму (правда, это везде нужно, но суть не в этом), затем инициализировать движок плагина, который в обязательном порядке привяжет кучу скриптов к элементам формы. И уже после этого вызвать метод isValid, который не всегда доступен в явном виде. Иногда, этот метод еще нужно "как-то" вытащить из внутренних механизмов.

Примечание: С одной стороны, вполне логично, что привязка идет к тегу форм, так как плагины появились "после". С другой стороны, сегодня скрипты позволяют быстро создавать и собирать данные из любых произвольных форм. При определенном уровне любопытства и настойчивости, вы можете легко создать форму только на тэгах "span"(div) и "input", при этом выглядеть она будет точно так же, как и все другие.

Разумеется, большинство плагинов имеют много приятных моментов. Но, по большому счету, они сводятся к:

  • Наличию преднастроенных валидаторов. По сути, готовых регулярных выражений
  • Проверкам различных типов. Включая проверку данных нескольких полей (например, подтверждение пароля и т.п.)
  • Возможности создания собственных валидаторов
  • Красивой анимации со всплывающими окошками и появляющимися подсказками

Если раньше красивая анимация и навороченный механизм сами по себе были достаточно вескими аргументами в пользу использования того или иного плагина, то сегодня это уже не совсем так. Найти готовые регулярные выражения для проверок большинства известных типов полей - задача не очень сложная. Большое число разнотипных проверок уже не столь актуально, как раньше. Стремление к минимализму форм ведет к уменьшению количества проверок. Попробуйте вспомнить, когда вы в последний раз встречали проверки, которые бы отличались от валидации текста, исключая повторение пароля? Необходимость в создании собственных валидаторов, при обилии уже готовых регулярных выражений, встречается все реже и реже. Красивая анимация и всплывающие подсказки в окошках когда-то будоражили фантазию и придавали приложениям очарование и неповторимость. Тем не менее, сегодня, достаточно пометить красным цветом хорошо известные всем поля, и большинство пользователей сами поймут, что именно необходимо исправить. 

Примечание: Не стоит считать, что плагины не имеют смысла и что их не стоит использовать. Существует достаточно много ситуаций, когда написание кода с нуля обойдется намного дороже, чем использование готовых плагинов. В данной статье речь идет о тех ситуациях, когда ограничения плагинов либо создают препятствия, либо их плюсы не сильно превышают затраты по времени и силам.

 

Пишем скрипт для валидации произвольных форм на jQuery

Сноска: Вам совершенно не обязательно писать приложение с нуля, пример с готовым скриптом и стилями можно скачать по ссылке в конце статьи.

Прежде, чем реализовывать свой механизм, необходимо определиться с его возможностями и ограничениями:

  • Форма хранится в одном произвольном контейнере. Это может быть span, div, table и так далее. Важно то, что форма это отдельный элемент DOM. Сохранение данных с 15 разных контейнеров - это уже не обычная форма, с любой точки зрения.
  • Скрипт должен поддерживать как валидацию на лету, так и произвольную проверку любых форм или даже отдельных полей, без каких-либо привязок скриптов или других ограничений.
  • Плагин должен быть простым как с точки зрения настройки и запуска, так и с точки зрения организации и кода.
  • Необходимо, чтобы его можно было расширить в любое время. Добавить валидаторы. Встроить всплывающие сообщения. И многое другое.
  • Настройка валидаторов полей должна быть не только универсальной, но и простой

И первым делом, переходим к самому важному, к определению основного механизма - вызова проверок для полей. Ведь, выбранный алгоритм будет напрямую влиять не только на сложность настройки, но и на количество букв, которое вам придется набирать, чтобы его корректно запустить. Исходя из составленных требований, самым простым механизмом будет вызов валидаторов по наличию классов стилей в атрибуте "class". Во-первых, стили легко задаются и не накладывают серьезных ограничений. Во-вторых, их можно добавлять и убирать в любой момент. В-третьих, если какие-то классы стилей вы уже используете в своем проекте, то их переименование, как в скрипте валидации, так и в полях, решается простой заменой одной строки на другую, без кардинального изменения кода. 

Примечание: К сожалению, даже обилие javascript проверок и обработчиков событий не уберегут ни одно приложение от необходимости валидации данных на серверной стороне. Помните, что можно без особых усилий составить и вызвать отправку данных практически на любом браузере. 

Составим тестовый проект

Теперь, составим тестовый проект со следующей структурой:

  • css - каталог со стилями
    • template.css - файл стилей
  • images - каталог для картинок для стилей валидации
    • icon-fail.png - иконка для некорректно заполненного поля
    • icon-ok.png - иконка для правильно заполненного поля
  • js - каталог для скриптов
    • jquery-validate.js - скрипт валидации произвольных форм
  • index.html - тестовая форма

Примечание: При составлении структуры тестового проекта, старайтесь максимально приблизить ее к структуре настоящего проекта. Это поможет вам отловить проблемы, связанные со структурой, еще на этапе разработке.

Файл стилей - template.css

Большую часть файла стилей занимают стили самой формы, что бы демонстрационный пример выглядел привлекательно. Для самого же плагина нужны только первые два правила ".field-input.input-ok" и ".field-input.input-error"

Примечание: Старайтесь добавлять даже к тестовым формам хотя бы минимальны дизайн. Чем ближе тестовый проект будет к реальному, тем больше шансов отловить ошибки и скрытые проблемы. Например, картинка в "background" для проверенных полей будет занимать часть поля. Это означает, что для полей с большим объемом текста необходимо предусматривать отступы в стилях.

.field-input.input-ok{
    background: url(../images/icon-ok.png) no-repeat right center #e3ffe5;
    border: 1px solid #96b796;line-height: 19px;
}
.field-input.input-error{
    background: url(../images/icon-fail.png) no-repeat right center #ffebef;
    border: 1px solid red;line-height: 19px;
}
.clear{clear:both;}body{width: 1000px;margin:auto;}
.form{float: left;width: 490px;margin-right: 5px;}
.form .header{color: blue;font-size: 20px;font-family: georgia;margin-bottom: 10px}
.form .label{padding: 5px;padding-left: 0px;font-size: 18px;}
.form .field{margin-bottom: 10px;}
.form .field-input{padding: 5px;font-size: 15px;width: 400px;}
.form .save-button{cursor: pointer;margin-top: 10px;color: white;background: green;border: 1px solid #ddd;padding: 5px 10px;}
.form .save-button:hover{background: rgb(60, 176, 60);}

Файл скрипта валидации произвольных форм на jQuery - jquery-valiate.js 

Так как скрипт достаточно простой и в самих комментариях подробно описана вся необходимая информация, то файл приводится целиком:

 
(function (parentObject) {

    // Проверяем на повторную инициализацию

    if (parentObject.validation)

        return;

 

    ////////////////////////////////////////////////

    // Создаем общий интерфейс для валидации форм

    ////////////////////////////////////////////////

    parentObject.validation = {

        _defaultOptions: {

            // Контейнер. Может быть селектором или элементом

            container: '',

            // Селектор для поиска полей

            fieldSelector: '.field-input',

            // Проверка в режиме онлайн

            liveCheck: true,

            // Селектор для кнопок сабмита

            submitSelector: '.save-button',

            // Обработчик, который вызывается после успешной валидации

            submitHandler: null,

            // Класс полей с ошибками

            errorClass: 'input-error',

            // Класс верных полей

            okClass: 'input-ok',

            // Список валидаторов

            // Примечание: так как валидатор пустых полей вынесен отдельно,

            // то пустые строки в других валидаторах считаются как прошедшие

            validators: {

                'required': function (str) {

                    return str.length > 0;

                },

                'numeric': function (str) {

                    var regExp = new RegExp('\\-?\\d+((\\.|\\,)\\d{0,})?');

                    return regExp.test(str) || !str;

                },

                'alpha': function (str) {

                    var regExp = new RegExp('^[а-яА-ЯёЁa-zA-Z\\s]+$');

                    return regExp.test(str) || !str;

                },

                'alphanumeric': function (str) {

                    var regExp = new RegExp('^[а-яА-ЯёЁa-zA-Z0-9\\s]+$');

                    return regExp.test(str) || !str;

                },

                'date': function (str) {

                    var regExpISO = new RegExp('(19|20)\\d\\d-((0[1-9]|1[012])-(0[1-9]|[12]\\d)|(0[13-9]|1[012])-30|(0[13578]|1[02])-31)'),

                        regExpRU = new RegExp('((0[1-9]|[12]\\d)\\.(0[1-9]|1[012])|30\\.(0[13-9]|1[012])|31\\.(0[13578]|1[02]))\\.(19|20)\\d\\d');

                    return (regExpISO.test(str) | regExpRU.test(str)) || !str;

                },

                'datetime': function (str) {

                    var regExpISO = new RegExp('(19|20)\\d\\d-((0[1-9]|1[012])-(0[1-9]|[12]\\d)|(0[13-9]|1[012])-30|(0[13578]|1[02])-31) (\\d+):(\\d+)'),

                        regExpRU = new RegExp('((0[1-9]|[12]\\d)\\.(0[1-9]|1[012])|30\\.(0[13-9]|1[012])|31\\.(0[13578]|1[02]))\\.(19|20)\\d\\d (\\d+):(\\d+)');

                    return (regExpISO.test(str) | regExpRU.test(str)) || !str;

                },

                'time': function (str) {

                    var regExp = new RegExp('(\\d+):(\\d+)');

                    return regExp.test(str) || !str;

                },

                'digit': function (str) {

                    var regExp = new RegExp('^[0-9]+$');

                    return regExp.test(str) || !str;

                },

                'password': function (str) {

                    var regExp = new RegExp('^[а-яА-ЯёЁa-zA-Z0-9\\s]+$');

                    return regExp.test(str) || !str;

                },

                'email': function (str) {

                    var regExp = new RegExp('^([a-z0-9_-]+\\.)*[a-z0-9_-]+@[a-z0-9_-]+(\\.[a-z0-9_-]+)*\\.[a-z]{2,6}$');

                    return regExp.test(str) || !str;

                },

                'url': function (str) {

                    var regExp = new RegExp('^((https?|ftp)\\:\\/\\/)?([a-z0-9]{1})((\\.[a-z0-9-])|([a-z0-9-]))*\\.([a-z]{2,6})(\\/?)$');

                    return regExp.test(str) || !str;

                }

            }

        },

 

        // Функция инициализации

        // Создает каркас для форм

        init: function (_options) {

            var options = $.extend({}, this._defaultOptions, (_options || {})),

                self = this;

 

            // Если указан контейнер (или его селектор), 

            // а так же селектор для полей,

            // то создаем каркас

            if (options.container && options.fieldSelector) {

                // Если есть селектор для кнопок,

                // то вещаем обработчик на щелчек

                if (options.submitSelector) {

                    $(options.container).find(options.submitSelector).on('click', function () {

                        if (self.isValid(options)) {

                            // Если указан обработчик, после успешной валиадции,

                            // то вызываем его

                            if (typeof (options.submitHandler) === 'function')

                                options.submitHandler();

                            return true;

                        }

                        else {

                            return false;

                        }

                    });

                }

 

                // Если нужна проверка в режиме онлайн

                if (options.liveCheck) {

                    // Проходимся по всем полям и вешаем проверку валидности

                    $(options.container).find(options.fieldSelector).each(function (cnt, item) {

                        $(item).on('click', function () {

                            self.validItem($(item), options)

                        }).on('blur', function () {

                            self.validItem($(item), options)

                        }).on('change', function () {

                            self.validItem($(item), options)

                        }).on('keyup', function () {

                            self.validItem($(item), options)

                        });

                    });

                }

            }

        },

 

        // Функция для валидации отдельного элемента

        validItem: function (item, _options) {

            var options = $.extend({}, this._defaultOptions, (_options || {})),

                classList = $(item).attr('class').split(/\s+/),

                validResult = true;

            // Проходимся по всем классам в атрибуте "class", 

            // и если находим класс с именем валидатора, то вызываем его

            $.each(classList, function (index, cl) {

                // Проверка для повторяющихся полей,

                // имя поля, которое должно повториться указывается в атрибуте "confirm-field"

                if (cl === 'confirmfield') {

                    validResult 

                        &= ($(options.container).find('[Name="' + $(item).attr('confirm-field') + '"]').val()

                            == $(item).val());

                }

                // Иначе обычная проверка

                else if (typeof (options.validators[cl]) === 'function') {

                    validResult &= options.validators[cl](item.val());

                }

            });

 

            // Если поле не прошло валидацию

            if (!validResult)

                $(item).addClass(options.errorClass).removeClass(options.okClass);

            // Поле прошло валидацию

            else

                $(item).addClass(options.okClass).removeClass(options.errorClass);

 

            // Возвращаем результат

            return validResult;

        },

 

        // Проверка всех полей произвольной формы

        isValid: function (_options) {

            var options = $.extend({}, this._defaultOptions, (_options || {})),

                validResult = true,

                self = this;

            // Если указан контейнер (или его селектор), а так же селектор для полей,

            // то проверяем все поля

            if (options.container && options.fieldSelector) {

                $(options.container).find(options.fieldSelector).each(function (cnt, item) {

                    validResult &= self.validItem($(item), options);

                });

            }

 

            // Возвращаем результат проверки

            return validResult;

        },

 

        // Очищаем поля от всех классов стилей, которые были добавлены во время проверки

        clear: function (_options) {

            var options = $.extend(true, {}, this._defaultOptions, (_options || {}));

            if (options.container && options.fieldSelector) {

                $(options.container).find(options.fieldSelector)

                .removeClass(options.errorClass).removeClass(options.okClass);

            }

        }

    }

})(window);

Тестовая страница с формами - index.html

Тестовая страница достаточно проста. Ее основное пространство занимает html-код самих форм демонстрационного примера. Сам же вызов плагина для создания валидации на лету, а так же запуск ручной валидации по нажатию на кнопку, занимают всего несколько строк. 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html>

    <head>

        <meta http-equiv="content-type" content="text/html; charset=utf-8">

        <link href="/css/template.css" rel="stylesheet" type="text/css"/>

 

        <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>

        <script src="/js/jquery-validate.js"></script>

    </head>

 

    <script type=text/javascript>

        $(function () {

            // Валидация через механизм

            window.validation.init({

                container: '.form-1',

                submitHandler: function () {

                    alert('Валидация первой формы прошла успешно!');

                }

            });

            

            // Ручная валидация, доступная в любой момент

            $('.form-2 .save-button').on('click', function () {

                if(window.validation.isValid({ container: '.form-2' })) {

                    alert('Валидация второй формы прошла успешно!');

                }

            });

            

        });

    </script>

 

    <body>

        <br/>

        <br/>

        <br/>

        <div class="form-1 form">

            <div class="header">Валидации формы в режиме реального времени</div>

            <div class="label">Имя</div>

            <div class="field"><input type="text" name="UserName" class="field-input required alphanumeric" /></div>

            <div class="label">Email</div>

            <div class="field"><input type="text" name="Email" class="field-input required email" /></div>

            <div class="label">Пароль (цифры, буквы и пробел)</div>

            <div class="field"><input type="password" name="Password" class="field-input required password" /></div>

            <div class="label">Подтверждение пароля</div>

            <div class="field"><input type="password" name="CheckPass" class="field-input required confirmfield" confirm-field="Password" /></div>

            <div class="label">URL</div>

            <div class="field"><input type="text" name="Url" class="field-input url" /></div>

            <div class="label">Дата форматы ГГГГ-ММ-ДД и ДД.ММ.ГГГГ</div>

            <div class="field"><input type="text" name="Date" class="field-input date" /></div>

            

            <div class="clear"></div>

            <div class="btn-panel">

                <button class="save-button" >Отправить форму </button>

            </div>

        </div>

        <div class="form-2 form">

            <div class="header">Пример ручной валидации формы</div>

            <div class="label">Имя</div>

            <div class="field"><input type="text" name="UserName" class="field-input required alphanumeric" /></div>

            <div class="label">Email</div>

            <div class="field"><input type="text" name="Email" class="field-input required email" /></div>

            <div class="label">Пароль (цифры, буквы и пробел)</div>

            <div class="field"><input type="password" name="Password" class="field-input required password" /></div>

            <div class="label">Подтверждение пароля</div>

            <div class="field"><input type="password" name="CheckPass" class="field-input required confirmfield" confirm-field="Password" /></div>

            <div class="label">URL</div>

            <div class="field"><input type="text" name="Url" class="field-input url" /></div>

            <div class="label">Дата форматы ГГГГ-ММ-ДД и ДД.ММ.ГГГГ</div>

            <div class="field"><input type="text" name="Date" class="field-input date" /></div>

            

            <div class="clear"></div>

            <div class="btn-panel">

                <button class="save-button" >Отправить форму </button>

            </div>

        </div>

 

    </body>

</html>

Теперь, сохраняем все файлы в проекте, открываем index.html в любом браузере и переходим к первичному тестированию результата.

 

Смотрим результат выполнения скрипта валидации на jQuery в тестовом проекте

После того как вы откроете файл, перед вами должна появится следующего вида страница:

Составленные формы для тестового проекта - Валидация полей произвольных форм и контейнеров на jQuery 

Как видно, на экране будут отображаться две формы. Форма следа будет проверяться на валидацию в режиме реального времени, т.е. при каждом вводе в текстовые поля, а так же при нажатии на кнопку "отправить форму". Форма справа будет проверяться вручную, только при нажатии на кнопку "Отправить форму". В остальное время форма останется нетронутой. 

Теперь, убедимся, что валидация происходит в те моменты, когда это необходимо, и что формы никак не влияют друг на друга. Выглядеть это будет примерно так:

Тестирование форм с валидацией - Валидация полей произвольных форм и контейнеров на jQuery

Как видно, в форма слева проводилась валидация на лету. А в форме слева валидация происходила только по нажатию на кнопку "Отправить форму."

Закончив, первичное тестирование, необходимо снова пройтись по возможностям и ограничениям составленным в самом начале:

  • Форма хранится в одном произвольном контейнере. - Выполнено. Форма не зависит от конкретного элемента. Обрабатывает произвольный контейнер
  • Валидация как на лету, так и вручную. - Выполнено. Валидацию можно производить как на лету, так и вручную
  • Плагин должен быть простым - Выполнено. Код очень простой. Настройка форм заняла не более 5 минут. Для запуска плагина понадобилось всего пара строчек
  • Можно было расширить в любое время - Выполнено. Добавление валидаторов элементарное. Можно добавить как в опции по умолчанию, так и во время запуска плагина. Так как код простой, то добавить функциональность то же просто.
  • Настройка валидаторов должна быть универсальной и простой - Выполнено. Валидаторы указываются через классы стилей. 

Теперь, у вас есть легкий и простой плагин на jQuery, который можно применять для валидации произвольных форм как на лету, так и в ручном режиме. При этом, плагин легко расширяется как с точки зрения количества проверок, так и с точки зрения дополнительной функциональности.

Ссылка для скачивания плагина с тестовым проектом находится тут:

validate.zip 

Социальные сети

☕ Понравился обзор? Поделитесь с друзьями!

Социальные сети
Программы (Freeware, OpenSource...)