Задача: сделать универсальную функцию для прохождения по полям формы с возможностью проверки введенных данных
Действительно, удобнее проверять форму пока она не ушла на сервер, чем после отправки возвращать пользователю HTML-страницу с той же формой и списком ошибок (хотя серверная проверка необходима, из соображений безопасности и на случай отключенного JavaScript). Попытаемся набросать JavaScript-функцию, которая будет проходить по всем элементам формы, определять их тип и совершать действия по проверке данных. В качестве аргумента функция примет ссылку на саму форму. Удобнее всего её вызывать по событию onsubmit.
Форма:
<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-функции:
// Заранее объявим необходимые переменные
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-функции
Замечание
Еще удобнее проверять форму не при попытке отправки, а при вводе данных. На фасткодере это реализовано в большинстве форм, но это тема для отдельной статьи :-)

Когда что-то пишем в поле, валидатор ненавязчиво намекает, что не так текстом из errorString (его можно проэкстендить своим). Если теряет фокус и есть ошибки (не матчит regexType), то добавляет div.reg_list (по-умолчанию) класс alert (тоже по-умолчанию), все это можно поменять в user_options.
имена свойств из errorString и regexType дожны совпадать с классами проверяемых инпутов
user_options - опции, которые можно настроить под свой вид
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;
Вместе со ссылкой на отдельный js-файл вместо полотна кода ваш комментарий будет более содержательным .
Вероятно, у вас проблема с чем-то еще. По коду скрипта могу сказать, что добавление новых типов элементов в форму не должно влиять на его работоспособность.
Вот упрощенный код станички:
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-ом все прекрасно работает.
Элемент fieldset попадает в цикл как элемент формы. У него нет св-ва value, поэтому в ф-ию trim уходит undefined и возникает ошибка. Фикс простой:
И еще совет. В вашем коде тэг <form> стоит между <table> и <tr>. Лучше его отттуда убрать и поставить перед тэгом <table>, иначе могу быть проблемы при отправке в гекко-браузерах. По-возможности пишите валидный (X)HTML-код.
И последний вопрос, если можно:
Будет ли данная статья? Было бы очень интересно почитать.
Добавил в todo, но не уверен что будет скоро. Есть более срочные статьи, да и времени для статей, честно говоря, с каждым днем меньше. Подпишитесь на новые статьи в разделе JavaScript, как только добавлю - вам придет уведомление.