Валидатор html форм
Всем привет. В этой статье я хочу выложить свеже-написанный скрипт валидации форм и объяснить, как с ним работать. Данный скрипт позволяет валидировать данные, введенные пользователем в html - форму страницы перед отправкой их на сервер, предоставляя для этого удобный и расширяемый функционал. Что в нём особенного?
- Он представляет собой единый модуль и не засоряет собой пространство имен.
- Прост в интеграции.
- Работает во всех основных браузерах, включая линейку IE от 6-ой до 9-ой версии
- НЕ использует JQuery
- НЕ нарушает web стандарты
- Мало вЕсит
- Указывает ошибки валидации рядом с полем ввода.
- Имеет возможность расширения/индивидуальной настройки.
- Настраиваемая мультиязычность
Мечта ... блин!
Обновлено 14-05-2012 Листинг ниже остался прежним, все изменения и пример смотрим в архиве.
- Исправлена ошибка, когда в форме применяются fieldset-ы
- Теперь скрипт умеет проверять значения указанных полей на идентичность(когда требуется подтверждение пароля например)
Архив с обновленным скриптом и примером можно скачать тут
Вот рабочий пример:
В начале сделаю небольшое пояснение. Скрипт позволяет проверять значения текстовых полей типа INPUT и TEXTAREA. На сколько я могу судить об остальных элементах форм, то они, как раз разработаны таким образом, что пользователь может выбрать или установить только заранее определённые значения. Это элементы типа SELECT, CHECKBOX, RADIOBUTTON и т.д. - значения этих элементов, как правило задаются разработчиками, и по моему скромному мнению не нуждаются в валидации, по крайней мере, если использовать их правильно.
Конечно другие JQ - монстры с подобным предназначнием позволяют валидировать хоть чёрта лысого, в том числе и радиокнопки, но по-моему это уже перебор. Вес у тех товарищей конечно соответственный + конечно необходимость наличия самой JQ. Плагин JQ validate несомненно более продвинутый, и более сложен чем мой, НО и размер у него больше.
Ниже я привёл листинг объекта валидатора.
Валидатор html форм:
/** * Объект "validator" */ var validator = (function( GLOB ){ return { // Объект для тестов. Здесь он пустой // Наполняется он в файле validator_tests.js tests : {}, /** * Метод для валидации формы. * Вешается на обработчик onsubmit * @param object form: Объект html формы. * @return boolean true|false */ check : function ( form ) { var DOC = GLOB.document, // Зависимость elements = form.elements, // Здесь будут элементы формы elLength = elements.length, // Количество элементы формы require = false, // Флаг: обязательно ли поле curItem = null, // Текущий (для цикла) элемент формы curVal = null, // Текущее (для цикла) значение элемента формы curTest = null, // Текущий (для цикла) класс/тест элемента формы errSpan = null, // Контейнер для ошибок элемента формы tests = [], // Здесь будут классы/тесты элемента формы errors = {}, // Флаг указывает есть ли ошибки prop = null, // Своство для обхода объекта errors testsLength = 0, // Количество классов/тестов элемента формы i, // Счётчики циклов q; for (i = 0; i < elLength; i += 1) { // Получаем текущий элемент: curItem = elements[i]; // Получаем текущее значение: curVal = curItem.value; // Пропускаем элементы не имеющие классов/тестов: if (typeof (curItem.className) === "undefined") { continue; } // Узнаём обязателен ли текущий элемент: require = (curItem.className.indexOf("require") !== -1); // Пытаемся получить ссылку на элемент-контейнер ошибок: errSpan = DOC.getElementById(curItem.name + "_error"); // Если элемента-контейнера не существует if (!errSpan) { // ... формируем его: errSpan = DOC.createElement("SPAN"); errSpan.id = curItem.name + "_error"; errSpan.className = "error"; // и добавляем его в DOM - древо, // сразу после текущего элемента формы. curItem.parentNode.insertBefore(errSpan, curItem.nextSibling); } // Если текущий элемент не обязателен и не заполнен... if (curVal.length < 1 && !require) { // Очищаем его контейнер, на случай, если он уже содержал текст ошибки, errSpan.innerHTML = ""; // и пропускаем итерацию цикла. continue; } // Получаем имена классов/тестов в массив: tests = curItem.className.split(" "); // Получаем длину массива: testsLength = tests.length; // Проходим по массиву классов/тестов циклом: for (q = 0; q < testsLength; q += 1) { // Получаем текущее имя класса: curTest = tests[q]; // Если текущее имя класса не является тестом... if (!this.tests.hasOwnProperty(curTest)) { // пропускаем итерацию. continue; } // Собсна проверка: if (!curVal.match(this.tests[curTest].condition)) { // Устанавливаем флаг для этой ошибки: errors[curItem.name] = true; // Не удачно - пишем ошибку в контейнер: errSpan.innerHTML = this.tests[curTest].failText; // Останавливаем цикл - вывод остальных ошибок для этого элемента не нужен, // - не зачем пугать пользователя, пусть сначала устранят ту ошибку что есть. break; } else { // Снимаем флаг ошибки: errors[curItem.name] = false; // Удачно - очищаем контейнер от содержимого. errSpan.innerHTML = ""; } // END IF } // END FOR } // END FOR /* * Проверяем наличие установленных флагов ошибок. * Если ошибок нет возвращаем true - в этом случае * Обработчик "onsubmit" должен штатно отработать. */ for( prop in errors ) { if ( errors.hasOwnProperty(prop) && errors[prop] === true) { return false; } } return true; } }; }(this));
Как всегда я старался писать как можно проще и меньше - если убрать комментарии то он занимает 62 строчки... меньше не получилось
И так, вы можете скачать архив и покопаться там самостоятельно, а можете почитать статью здесь я подробно объясню, что да как. Вобщем давайте попробуем поработать с моим валидатором.
По-сути сам объект "validator" состоит из двух вещей - это внутренний объект: tests, который содержит внутри себя тесты, и собственно сам метод валидации: check, который принимает ссылку на объект html формы и возвращает TRUE или FALSE в зависимости от результата валидации.
Для начала создадим html форму, такого вида (я постарался задействовать все реализованные тесты):
<fieldset> <legend>Пример формы</legend> <form id="myform" action="#" method="post" name="myform"> <div> <label for="login">Обяз., англ. буквы и пробелы:</label> <input id="login" type="text" name="login" class="require_ws en_char something somewhere" /> </div> <div> <label for="first_name">Обяз., рус. букы и пробелы:</label> <input id="first_name" type="text" name="first_name" class="require ru_char something somewhere" /> </div> <div> <label for="last_name">Не обяз., рус. букы и пробелы:</label> <input id="last_name" type="text" name="last_name" class="ru_char" /> </div> <div> <label for="mynumeric">Не обяз., целые и веществ. числа:</label> <input id="mynumeric" type="text" name="mynumeric" class="numeric" /> </div> <div> <label for="myinteger">Обяз., целые числа:</label> <input id="myinteger" type="text" name="myinteger" class="require integer" /> </div> <div> <label for="mycred_card">Обяз., кредит. карта:</label> <input id="mycred_card" type="text" name="mycred_card" class="require cred_card" /> </div> <div> <label for="email_addr">Обяз., email адрес</label> <input id="email_addr" type="text" name="email_addr" class="require email" /> </div> <div> <label for="myipv4">Обяз., IPv4 адрес</label> <input id="myipv4" type="text" name="myipv4" class="require ipv4" /> </div> <div> <label for="mydomain">Обяз., доменное имя</label> <input id="mydomain" type="text" name="mydomain" class="require domain" /> </div> <div> <label for="notice">Обяз., рус, без спец. символов</label> <textarea id="notice" name="notice" class="require_ws ru_char no_special_chars" ></textarea> </div> <div class="centered"> <button name="send_form" type="submit">Отправить</button> </div> </form>
В названиях классов для элементов INPUT и TEXTAREA размещаются названия нужных нам тестов, пройдя которые, поле считается корректно заполненным. Присмотритесь к примеру ниже:
... <input id="first_name" type="text" name="first_name" class="require ru_char something somewhere" /> ... <input id="last_name" type="text" name="last_name" class="ru_char" /> ... <input id="phone" type="text" name="phone" class="digits" /> ... <input id="email_addr" type="text" name="email_addr" class="require email" /> ... <textarea id="notice" name="notice" class="require en_char no_special_chars" ></textarea> ...
Эти самые тесты на все случаи жизни написать не возможно, поэтому я сделал возможность для пользователей писать эти тесты самостоятельно, под собственные нужды. Конечно несколько распространенных тестов я всё же написал за вас, но и их вы можете переопределить. Я специально вынес их в отдельный файл: validator_tests.js который нужно подключить после основного: validator.js (или сжатая версия: validator.pack.js) Там же, в файле validator_tests.js - тексты ошибок можно переписать на любом языке - вот Вам ещё и мультиязычность
Тест, как таковой, представляет собой простую конструкцию - это объект содержащий всего два свойства, я назвал их соответственно failText - "текст ошибки" и condition - "условие". Ниже приведён код тестов для русских и английских букв из файла validator_tests.js:
/** * Проверка ввода русских букв и пробелов: */ validator.tests.ru_char = { failText : "Допустимы только русские буквы и пробелы.", condition : /^[а-яё\s]+$/i }; /** * Проверка ввода латинских букв и пробелов: */ validator.tests.en_char = { failText : "Допустимы только английские буквы и пробелы.", condition : /^[a-z\s]+$/i };
"Текст ошибки" - это строка, которую увидят пользователи, если значение в поле ввода не пройдёт валидацию, а "условие" - это регулярное выражение, которому должно соответствовать значение в поле ввода. Конечно, для того, чтобы писать под себя тесты, вы должны уметь составлять регулярные выражения, или у вас должен быть друг.., который умеет составлять регулярные выражения
Для того, что бы Вы понимали, под что вам нужно будет писать регулярки - приведу кусок псевдо-кода, где происходит проверка (оригинал кода это строка 74 в листинге валидатора):
if (!проверяемое_значение.match(this.tests[текущий_тест])) { // проверка НЕ пройдена ... } else { // проверка пройдена ... }
Видите результат работы метода match инвертируется? Принцип такой: если в значении подопытного поля не найдено, ничего, что противоречит шаблону, то это значение пройдёт проверку.
- ru_char - Допустимы только русские буквы и пробелы.
- en_char - Допустимы только английские буквы и пробелы.
- integer - Проверка ввода целых чисел.
- numeric - Проверка ввода целых, вещественных, положительных и отрицательных чисел.
- email - Проверка ввода email*
- require - Обязательное поле.
- require_ws - Обязательное поле без учёта пробелов.
- ipv4 - проверка ввода IPv4-адреса*
- domain - проверка ввода доменного имени*
- cred_card - Все основные кредитные карты*
В скрипте УЖЕ реализованы следующие тесты, вам нужно просто подставить их в атрибут class="" нужного поля :
* - Эти вещи частично или полностью заимствованы из библиотеки программы RegexBuddy( Не на правах рекламы - прикольная прога).
Далее подключаем файл скрипта - валидатора и тестов
<script type="text/javascript" src="validator.js"></script> <script type="text/javascript" src="validator_tests.js"></script>
и для примера определим ещё один, пользовательский тест - no_special_chars, он уже прописан для элемента TEXTAREA в html форме, но не существует в базовом варианте скрипта.
Допустим этот тест НЕ должен пропустить следующие символы: /?:@&=+$# ( тем более, что их не сможет закодировать encodeURI(), хотя их прекрасно кодирует encodeURIComponent() ).
Все пользовательские тесты должны стать свойствами объекта validator.tests, и должны реализоваться следующим образом :
<script type="text/javascript"> // Определяем собственный тест: validator.tests.no_special_chars = { failText : "Специальные символы не допустимы!", condition : /^[^\/?:@&=+$#]+$/im };
Теперь подключим наш валидатор к форме:
// Вешаем обработчик на событие отправки формы: document.myform.onsubmit = function(){ return validator.check(this); } </script>
Ну, вот по сути и всё! Надеюсь понятно объяснил. Пример вы видели в самом начале. - Пользуйтесь на здоровье :)
Информация копипастерам
Внимание! Копирование контента с сайта, возможно только с разрешения администратора. Т.е. Меня! Я скорее всего разрешу Вам это сделать, в обмен на живую ссылку, на статью оригинал.