Задайте свой вопрос о JavaScript на нашем форуме

JavaScript валидация (проверка) форм

Задача: сделать универсальную функцию для прохождения по полям формы с возможностью проверки введенных данных

Действительно, удобнее проверять форму пока она не ушла на сервер, чем после отправки возвращать пользователю HTML-страницу с той же формой и списком ошибок (хотя серверная проверка необходима, из соображений безопасности и на случай отключенного JavaScript). Попытаемся набросать JavaScript-функцию, которая будет проходить по всем элементам формы, определять их тип и совершать действия по проверке данных. В качестве аргумента функция примет ссылку на саму форму. Удобнее всего её вызывать по событию onsubmit.

Форма:

<form onsubmit="return checkForm(this);">
    <input type="hidden" />
    <fieldset>
        Имя: <input type="text" name="name" /><br/>
        E-mail: <input type="text" name="email" /><br/>
    </fieldset>
    <fieldset>
        <legend>Интересы:</legend>
        <input type="checkbox" name="inter[]" value="music" /> Music<br/>
        <input type="checkbox" name="inter[]" value="TV" /> TV<br/>
    </fieldset>
    <fieldset>
        <legend>Возраст:</legend>
        <input type="checkbox" name="age" id="age1" value="10-25" /> <label for="age1">10-25</label><br/>
        <input type="checkbox" name="age" id="age2" value="25-50" /> <label for="age2">25-50</label><br/>
        <input type="checkbox" name="age" id="age3" value="50+" /> <label for="age3">50+</label><br/>
    </fieldset>
    <fieldset>
        <legend>Любимое время суток:</legend>
        <select>
            <option value="0" selected="1">Выберите...</option>
            <option value="1">Утро</option>
            <option value="2">День</option>
            <option value="3">Вечер</option>
            <option value="4">Ночь</option>
        </select><br/>
    </fieldset>
    <fieldset>
        <legend>Комментарий:</legend>
        <textarea></textarea><br/>
    </fieldset>
    <fieldset>
        <legend>Прикрепить файл:</legend>
        <input type="file" name="name" /><br/>
    </fieldset>
    <input type="submit" value="Отправить" />
</form>

В форму вошло максималное число разнообразных полей.

Теперь код JavaScript-функции:

function checkForm(form) {
    // Заранее объявим необходимые переменные
    var el, // Сам элемент
        elName, // Имя элемента формы
        value, // Значение
        type; // Атрибут type для input-ов
    // Массив списка ошибок, по дефолту пустой
    var errorList = [];
    // Хэш с текстом ошибок (ключ - ID ошибки)
    var errorText = {
        1 : "Не заполнено поле 'Имя'",
        2 : "Не заполнено поле 'E-mail'",
        3 : "Не прикреплен файл",
        4 : "Не оставлен комментарий",
        5 : "Не выбрано любимое время суток"
    }
    // Получаем семейство всех элементов формы
    // Проходимся по ним в цикле
    for (var i = 0; i < form.elements.length; i++) {
        el = form.elements[i];
        elName = el.nodeName.toLowerCase();
        value = el.value;
        if (elName == "input") { // INPUT
            // Определяем тип input-а
            type = el.type.toLowerCase();
            // Разбираем все инпуты по типам и обрабатываем содержимое
            switch (type) {
                case "text" :
                    if (el.name == "name" && value == "") errorList.push(1);
                    if (el.name == "email" && value == "") errorList.push(2);
                break;
                case "file" :
                    if (value == "") errorList.push(3);
                break;
                case "checkbox" :
                    // Ничего не делаем, хотя можем
                break;
                case "radio" :
                    // Ничего не делаем, хотя можем
                break;
                default :
                    // Сюда попадают input-ы, которые не требуют обработки
                    // type = hidden, submit, button, image
                break;
            }
        } else if (elName == "textarea") { // TEXTAREA
            if (value == "") errorList.push(4);
        } else if (elName == "select") { // SELECT
            if (value == 0) errorList.push(5);
        } else {
            // Обнаружен неизвестный элемент ;)
        }
    }
    // Финальная стадия
    // Если массив ошибок пуст - возвращаем true
    if (!errorList.length) return true;
    // Если есть ошибки - формируем сообщение, выовдим alert
    // и возвращаем false
    var errorMsg = "При заполнении формы допущены следующие ошибки:\n\n";
    for (i = 0; i < errorList.length; i++) {
        errorMsg += errorText[errorList[i]] + "\n";
    }
    alert(errorMsg);
    return false;
}

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

Пример валидации формы с помощью JavaScript-функции

Замечание

Еще удобнее проверять форму не при попытке отправки, а при вводе данных. На фасткодере это реализовано в большинстве форм, но это тема для отдельной статьи :-)

Александр Бурцев 31 августа 2008

© Все права на данную статью принадлежат порталу fastcoder.org. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.

Комментарии

alpha 12 сентября 2008, 18:59 #
фреймворк для сложных форм - http://zforms.ru
 
romanoza 30 марта 2009, 20:49 #
Вот, дописал чей-то (не помню чей) валидатор на jQuery.
Когда что-то пишем в поле, валидатор ненавязчиво намекает, что не так текстом из errorString (его можно проэкстендить своим). Если теряет фокус и есть ошибки (не матчит regexType), то добавляет div.reg_list (по-умолчанию) класс alert (тоже по-умолчанию), все это можно поменять в user_options.
имена свойств из errorString и regexType дожны совпадать с классами проверяемых инпутов

user_options - опции, которые можно настроить под свой вид


jQuery.FormValidator = {
    options : {},
    errorString : {
        'vemail': 'Некорректный email',
        'vpassword': 'Пароль должен содержать от 6 до 20 символов из списка: А-я, A-z, 0-9, ! @ # $ % ^ & * ( ) _ - +',
        'vpasswordconfirm':'Пароли не совпадают',
        'vinvite': 'Некорректный код приглашения',
        'vname': 'Фамилия и имя могут содержать только русские буквы и иметь размер от 3-х до 32 символов',
        'vnick': 'Имя пользователя может содержать только латинские буквы и цифры и иметь размер от 5 до 32 символов',
        'vdomain': 'Допусткаются только латинские буквы и цифры и быть от 5 до 32 символов',
        'vdate': 'Формат даты дд.мм.гггг',
        'vcountry': 'Название страны может содержать буквы от А до я, цифры и символы - .',
        'vcity': 'Город может содержать буквы от А до я, цифры и символы - .`()'
    },
    regexType : {
        'vemail' : /^(("[\w-\s]+")|([\w-]+(?:\.[\w-]+)*)|("[\w-\s]+")([\w-]+(?:\.[\w-]+)*))(@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})]?$)/i,
        'vinvite': /^[a-fA-F0-9]{32}$/,
        'vpassword': /^[a-zA-Zа-яА-Я0-9\-\!\@\#\$\%\^\&\*\(\)\+_]{6,20}$/,
        'vname': /^[а-яА-Я\s]{3,32}$/,
        'vnick': /^[a-z0-9]{5,32}$/i,
        'vdomain':/^[a-z0-9]{5,32}$/i,
        'vcountry': /^[А-Яа-я\- ()\.`]{1,64}$/,
        'vcity': /^[А-Яа-я\- \.`()]{1,64}$/,
        'vdate': /^(0[1-9]|[12][0-9]|3[01])\.(0[1-9]|1[012])\.(19|20|21)\d\d$/
    },
    build : function(user_options)
    {

        var defaults = {
            ajax: true,
            validCheck: false,
            // Файл отправки данных формы
            phpFile:"register.php",
            // Класс, который назначается alertElem, когда значение поля невалидно
            alertClass:"alert",
            // Элемент, которому присваивается класс alertClass, когда значение поля невалидно
            alertElem:"div.reg_list",
            // Имя класса alertElem в случае невалидного значения поля !!!БЕЗ ТОЧКИ!!!
            alertElemClass:"reg_list",
            // Элемент, в котором отражается сообщение об ошибке из errorString
            infoElem:"div.info",
            // Элементы кнопки, которым назначается класс alertButtonClass в случае невалидного значения в одном из полей ввода
            buttonElem:".window-button",
            // Имя класса buttonElem в случае невалидных значений полей !!!БЕЗ ТОЧКИ!!!
            alertButtonClass:"window-button-black"
        };
        var $form = $(this);
        return $form.each(
            function() {
            jQuery.FormValidator.options = $.extend(defaults, user_options);
            var $inputs = [];
            var $thisForm = $(this);
            if(jQuery.FormValidator.options.validCheck){
                $inputs = $thisForm.find(":input").filter(":not(:submit)").filter(":not(:checkbox)").filter(":not(.alert)").filter(":not(:radio)").add($thisForm.find(":input").filter(":radio").filter(":checked"));

            }else{
                $inputs = $thisForm.find(":input").filter(":not(:submit)").filter(":not(:checkbox)").filter(":not(:radio)").add($thisForm.find(":input").filter(":radio").filter(":checked"));
            }

            $radio =
            //catch the submit
            $thisForm.submit(function(){
                //we need to do a seperate analysis for checboxes
                var $checkboxes = $thisForm.find(":checkbox");

                //we test all our inputs
                $nhidden = $inputs.filter(":not(:hidden)").filter(":not(:radio)");
                var isValid = jQuery.FormValidator.validateForm($nhidden);
                //if any of them come back false we quit
                if(!isValid){
                    return false;
                }
                if(jQuery.FormValidator.options.ajax){

                    var dataToSend = {};
                    $inputs.each(function(){
                        dataToSend[this.name] = this.value;
                    });
                    $checkboxes.each(function(){
                        if($(this).is(':checked')){
                            dataToSend[this.name] = this.value;
                        }else{
                            dataToSend[this.name] = "";
                        }
                    });
                    $.post(jQuery.FormValidator.options.phpFile, dataToSend, function(data){jQuery.FormValidator.dataResult(data)});
                    return false;
                }else{
                    return true;
                }
            });

            $inputs.bind("keyup", function() {  jQuery.FormValidator.validate.call(this,false); });
            $inputs.bind("change", function() {  jQuery.FormValidator.validate.call(this,true); });
            $inputs.filter("select").bind("change", function() { jQuery.FormValidator.validate.call(this,false); });
        });
    },

    checkIsAllOK : function (){
        var is_true = true;
        $(jQuery.FormValidator.options.alertElem).each(function(){
            var $thisInput = $("input",this);
            if($thisInput.attr("type")=="text" || $thisInput.attr("type")=="password")
                if($thisInput.val().replace(/^\s+|\s+$/g,"")=="" || $(jQuery.FormValidator.options.infoElem,this).hasClass("alert")) {
                    is_true = false;
                }
        });
        if(!is_true) {
            $(jQuery.FormValidator.options.buttonElem).addClass(jQuery.FormValidator.options.alertButtonClass);
        }
        else if(is_true) {
            $(jQuery.FormValidator.options.buttonElem).removeClass(jQuery.FormValidator.options.alertButtonClass);
        }
    },
    dataResult : function (data) {
        alert(data.toString());
    },

    makeAlert : function ($elem,mess, color) {
        while(!$elem.hasClass(jQuery.FormValidator.options.alertElemClass)){$elem = $elem.parent();}
        if(color){
            $elem.addClass(jQuery.FormValidator.options.alertClass);
        }
        $(jQuery.FormValidator.options.infoElem, $elem).addClass(jQuery.FormValidator.options.alertClass);
        if(typeof(mess)=="string")
            $(jQuery.FormValidator.options.infoElem, $elem).html(mess);

        jQuery.FormValidator.checkIsAllOK();
    },

    makeNormal : function ($elem,mess,color) {
        while(!$elem.hasClass(jQuery.FormValidator.options.alertElemClass)){$elem = $elem.parent();}
        if(color){
            $elem.removeClass(jQuery.FormValidator.options.alertClass);
        }
        $(jQuery.FormValidator.options.infoElem, $elem).removeClass(jQuery.FormValidator.options.alertClass);
        if(typeof(mess)=="string")
            $(jQuery.FormValidator.options.infoElem, $elem).html(mess);
       

        jQuery.FormValidator.checkIsAllOK();
    },
    /*
     * @return is the form valid
     */

    validateForm : function($inputs)
    {
        var isValid = true; //benifit of the doubt?
        $inputs.each(function(){jQuery.FormValidator.validate.call(this,true)});
        if($(jQuery.FormValidator.options.alertElem).hasClass(jQuery.FormValidator.options.alertClass)){isValid=false;}
        return isValid;
    },

    validate: function (color) {
        //password check for wether vpassword == vpasswordconfirm
        //var $password = $("#"+formId).find(".vpassword");
        //var $passwordconfirm = $("#"+formId).find(".vpasswordconfirm");
        var $this = $(this);
        var val = $this.val();
        var isValid = true;
        var errorMessage = "";
        var $thisObj = this;
        jQuery.each(jQuery.FormValidator.regexType, function(name, regexp) {
            if ($($thisObj).hasClass(name)) {
                if(!regexp.test(val)){
                    isValid = false;
                    errorMessage = jQuery.FormValidator.errorString[name];
                }
            }
        });
        //Check for not empty empty
        var checkValue;
        if($this.hasClass('vpassword')){
            $el = $this;
            while($el.attr("tagName").toLowerCase() != "form"){$el = $el.parent();}
            $el = $el.find(".vpasswordconfirm");
            checkValue = $el.val();
            if(val != checkValue && isValid){
                jQuery.FormValidator.makeAlert($el, jQuery.FormValidator.errorString["vpasswordconfirm"], false);
            }
            else {
                jQuery.FormValidator.makeNormal($el, "", false);
            }
        }else
        if($this.hasClass('vpasswordconfirm')){
            //we need to find the other password field and check it
            $el = $this;
            //locate the form so we can search for the other field
            while($el.attr("tagName").toLowerCase() != "form"){$el = $el.parent();}
            $el = $el.find(".vpassword");
            //store text of other password field
            checkValue = $el.val();
            //comapre and set the other to red if appropriate, or green
            if(val != checkValue){isValid= false;
                errorMessage = jQuery.FormValidator.errorString["vpasswordconfirm"];
            }
        }
        if(!isValid){
            jQuery.FormValidator.makeAlert($this, errorMessage, color);
        }else{
            jQuery.FormValidator.makeNormal($this, "", true)
        }
    }
}
jQuery.fn.validateForm = jQuery.FormValidator.build;
 
Bur 31 марта 2009, 10:57 #
А можно ссылочку на работающий пример?
Вместе со ссылкой на отдельный js-файл вместо полотна кода ваш комментарий будет более содержательным .
 
zned 27 мая 2009, 21:32 #
Александр спасибо вам огромное за отличный скрипт, единственное не очень хочет работать он с формами в которых есть <fildset> и <legend>. Не подскажите как решить проблемку?
 
Bur 28 мая 2009, 12:02 #
Добавил в форму из статьи элементы <fieldset> (вы неправильно записали тэг), <legend> и <label>. Все прекрасно работает, убедиться можно на примере.
Вероятно, у вас проблема с чем-то еще. По коду скрипта могу сказать, что добавление новых типов элементов в форму не должно влиять на его работоспособность.
 
zned 28 мая 2009, 14:39 #
Спасибо, что откликнулись Александр, но у меня все равно с полем fieldset отказывается работать.
Вот упрощенный код станички:

<SCRIPT type=text/javascript>
function trim(str){var str=str.replace(/^\s\s*/,''), ws=/\s/, i=str.length; while(ws.test(str.charAt(--i))); return str.slice(0,i+1);}
function checkForm(form)
{//Заранее объявим необходимые переменные
var el,//Сам элемент
elName,//Имя элемента формы
value,//Значение
type;//Атрибут type для input-ов
var errorList=[];//Массив списка ошибок, по дефолту пустой
var errorText={//Хэш с текстом ошибок (ключ - ID ошибки)
1:"Не заполнено поле «Описание»"}
for(var i=0; i<form.elements.length; i++)//Получаем семейство всех элементов формы
{
el=form.elements[i];//Проходимся по ним в цикле
elName=el.nodeName.toLowerCase();
value=trim(el.value);//Удаляем пробелы
if(elName=="input"){}//INPUT Ничего не делаем, хотя можем
else if(elName=="textarea"){if(el.name=="info" && value=="")errorList.push(1);}//TEXTAREA
else if(elName=="select"){}//SELECT Ничего не делаем, хотя можем
else {}// Обнаружен неизвестный элемент ;)
}
// Финальная стадия
if(!errorList.length)return true;// Если массив ошибок пуст - возвращаем true
var errorMsg="ОШИБКА ЗАПОЛНЕНИЯ ФОРМЫ:\n\n";// Если есть ошибки - формируем сообщение, выовдим alert и возвращаем false
for(i=0; i<errorList.length; i++){errorMsg += errorText[errorList[i]] + "\n";} alert(errorMsg); return false;
}
</SCRIPT>


<table><form name='form' method='post' onsubmit='return checkForm(this);'>
<tr><th>Описание</th><td><textarea name='info'></textarea></td></tr></table>

<fieldset><legend>Заголовок</legend>Какие-то другие данные</fieldset><!-- БЕЗ ЭТОЙ СТРОЧКИ ВСЕ РАБОТАЕТ -->

<br><input name='submit' type='submit' id='submit' value='ОТПРАВИТЬ'></form>


Обращаю внимание на то, что без строчки с fieldset-ом все прекрасно работает.
 
Bur 28 мая 2009, 14:50 #
Дело вот в этой строчке (которой нет в моем оригинальном коде :-)
value=trim(el.value);

Элемент fieldset попадает в цикл как элемент формы. У него нет св-ва value, поэтому в ф-ию trim уходит undefined и возникает ошибка. Фикс простой:
if ( el.value != undefined ) value=trim(el.value);

И еще совет. В вашем коде тэг <form> стоит между <table> и <tr>. Лучше его отттуда убрать и поставить перед тэгом <table>, иначе могу быть проблемы при отправке в гекко-браузерах. По-возможности пишите валидный (X)HTML-код.
 
zned 28 мая 2009, 15:21 #
Еще раз спасибо вам Александр, теперь все замечательно работает.
И последний вопрос, если можно:
Цитата:
Еще удобнее проверять форму не при попытке отправки, а при вводе данных. На фасткодере это реализовано в большинстве форм, но это тема для отдельной статьи :-)

Будет ли данная статья? Было бы очень интересно почитать.
 
Bur 28 мая 2009, 15:26 #
Ох :-)
Добавил в todo, но не уверен что будет скоро. Есть более срочные статьи, да и времени для статей, честно говоря, с каждым днем меньше. Подпишитесь на новые статьи в разделе JavaScript, как только добавлю - вам придет уведомление.
 
 
Rambler's Top100 Flede HTML valid CSS valid