Универсальный скрипт для валидации формы

 
0
 
JavaScript
ava
Sardar | 09.11.2004, 18:16
Очень часто нужно проверить форму перед отправкой. Часто задаются вопросы "как?" и эти вопросы преживут всех нас вместе взятых smile
Сегодня днём меня выгнали из лаборатории, уборка, потому свободен, а от свободы руки чешутся.

Представим что при вёрстке формы мы можем задать правила, на то, какие значения допустимы в этом текстовом поле, интерактивная(при вводе) проверка(например только числа), минимум чекбоксов и какие должны быть отселектированны, что бы отправить форму и прочее. Назовём это механизм встроенных(inline) правил.

После загрузки документ начинает контролироватся скриптом, у скрипта вся необходимая инфа что делать в различных ситуациях, от простых alert'ов, до сложных вычислений. Назовём эту инфу просто "Описание".

[dohtml]<h4>Пара слов о inline правилах</h4>[/dohtml]
Нужен некоторый синтаксис для описания правил. Не будем изобретать велосипед и сидеть неделями за докозательствами тех или иных решений. Возьмем за основу синтаксис CSS:
<input type="text" validator="validate: interactive; allow-chars:[0-9A-Fa-f]; disallow-chars:[CB];">

Мы определили 3 правила: проверка в момент ввода, допустимые символы и не допустимые символы.

Что же должно произойти в случае не правильного ввода? Проверка в момент ввода уже подразумевает не остановку отправки формы, значит нужно обьявить какое нибудь поведени, иначе ничего не произойдёт. Читаем об этом ниже.

Писать для каждого поля свой набор правил трудоёмкая задача, потому будем также определять общие (shared) правила в Описании. Эти правила затем вставляются в inline правила.
Отсюда сделаем первый вывод: существует помимо механизма inline правил еще и набор комманд, например import, которую только что рассмотрели. Придумаем для этого еще один аттрибут элемента: validatorCommand.

Вот и первые вопросы. Давайте опеделим как можно больше ситуаций(что нужно сделать) с которыми мы встречаемся при проверке формы. Отсюда мы придумаем набор правил, комбинации которых позволят охватить всю широту темы

[dohtml]<h4>Пара слов о Описании</h4>[/dohtml]
В описании мы будем определять общие правила и действия, для конкретных ситуаций.
Описание может быть определенно в:
  • в JS коде - это больше для старых версий бразуеров(убогая Opera 6.xx и подобные). Будем старатся поддержать их, но жертвовать мыслью ради них не будем, каждый может апгрейдится хотя бы раз в 3 года.
  • в span/div элементе, его текст парсится как и текст в JS коде - хорошо бы закинуть это в одтельный тег <validator> в заголовке, но не все бразуеры(Опера) будут поддерживать левые теги.
  • в HTML/XML коде, который прячем от глаз пользователя - обьемный и не очень удобный способ, но можно отработать до совершенства.
Давайте больше подумаем над этим.

В 90% случаем нужно просто не отправить форму, но нужны также и расширенные возможности. В основном в описании будут указыватся пользовательские функции, но также реализуем несколько наиболее частых решений. Например чекбоксы доступны только если только radiobutton отселектирован(подвод элементов под паттерн).

Синтаксис должен быть простым и не утомительным как для человека, так и для парсера.
Над этим нужно основательно подумать.
Для примера выше можно определить действие в случии не правильного ввода и связать это действие с элементом:
<input type="text" validator="validate: interactive; allow-chars:[0-9A-Fa-f]; disallow-chars:[CB];" validatorCommand="handler: myAlertHandler">

handler myAlertHandler:alert(
"Вы ввели не число: " + character;
)

Этот пример говорит: при ошибке ввода вызывай функцию alert(она стандартная, скрипту безразлично), передай ей аргументы(сторку). В аргументах встречатются простые операции.
Синтаксис кажется сложным? ;-) На самом деле это не так. У кого какие идеи будут?
Как видим этот способ позволяет вызвать любую функцию с некоторыми аргументами, при этом аргументы можно задать в зависимости от ситуации. В контексте handler существует несколько переменных связанных с элементом и ситуацией.

Время выходит, вечером допишу остальное. Я вывожу основу/идеюм которая всегда обьемна. В использовании это всё будет компактным и удобным. Призываю всех присоединится к проекту, задачка интересна и увлекательна smile
Kommentare (13)
ava
sergejzr | 09.11.2004, 18:20 #
А рэгами условие на проверку задать нельзя? (Сорри, я в них не разбирался ещё)
ava
Sardar | 10.11.2004, 00:54 #
Цитата (sergej @ 9.11.2004, 17:20)
А рэгами условие на проверку задать нельзя? (Сорри, я в них не разбирался ещё)

А как раз зачастую регами и будет проверятся ввод ;-)
Идея в некотором универсальном механизме валидации формы, не задымываясь над JS кодом. Синстаксис и общая идея почти как у CSS стилей, мы задаем правила, которые описывают конкретный элемент. В зависимости от типа элемента бдует выполнятся валидация.
Вообще идея составить спецификацию на подибии CSS, а затем реализовать её.
ava
Alx | 10.11.2004, 17:59 #
ну что ж идея очень интересная и заманчивая! :)

1. если мы задаем аттрибут для тегов формы, то когда будет вызываться сам валидатор? перед отпракой/во премя изменения/после изменения?

2. как эти строчки отностится к JavaScript?

handler myAlertHandler:alert(
"Вы ввели не число: " + character;
)


3. по поводу правил интерпретации наших установок: по-моему лучше всего сделать JavaScript-код наподобии CSS, т.е. правила могут задаваться непосредственно в атрибуте validator, который по аналогии равен style. Так же можно задавать атрибут class, пусть он будет vclass.
HTML
<input type="text" vclass="txt1">

в нашем JavaScript-коде будет следуещее:

<script>
// встроенные объекты
var input_file = "ext:.gif,.jpg,.png,.html;dir:uploads/;";
var ...
// классы, содаваемые юзером
var _txt1 = "allow-chars:[0-9A-Z];disallow-chars:[6];";
var _uplHTML = "ext:.html;dir:uploads/pages";
</script>


вот.
т.е. при разборе скрипта основной функцией, если задан vclass txt1, то мы не пропустиим русские буквы, символы и циферку 6. если же ничего не будет указано, то будем действовать по умолчанию для input_text.

Я правильно тебя понял?
ava
Sardar | 10.11.2004, 23:26 #
Не совсем, но хорошо что ты откликнулся smile Будем развивать идею до готовой логичной спецификации, а потом имплементируем. Код как он у меня сейчас "в голове" очень компактный.

Мы работаем только с формами, т.е. только с вводом. Конечно основой нашей "библиотечки" будут общие функции/обьекты, так что программист может подключить нужный ему код.

Синтаксис inline правил почти точная копия CSS, только мы описываем как форма будет валидироватся. Я ушел дальше аттрибута class в CSS, теперь мы имеем аттрибут validatorCommands, в котором мы описываем специфику проверки, не правила.

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

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

Когда же проверять элемeнт/форму? Мы абстрагируемся от событий и выведем две ситуации:
  • В момент отправки формы - родительская форма перебирает свои элементы складывая результаты. форма отправляется если все тесты прошли успешно. Назовём ситуацию passive
  • В момент ввода - ввод у каждого типа элемента разный, например у чекбокса, это смена состоянния, у текстового поля это смена значения и нажатие кнопки. Назовём ситуацию interactive
  • Третья ситуация нужна для скриптов - ввод не проверяется вообще, назовём её none.
Теперь мы можем задать допустимые символы для текстовых элементов и допустимое содержание в момент отправки.

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

Пользовательские обработчики
Но стандартных действий не достаточно, будем ресширять пользовательским кодом. Для этого существуют обработчики состояний, таких у нас три вида:
  • handler-filter - запускается перед проверкой. Приводит, если надо, элемент в корректное состояние. Обычно этот обработчик не нужен.
  • handler-pass - запускается в случае успешной проверки.
  • handler-fail - синоним handler, запускается в случае не удачной проверки, контекст и свойства одинаковые как у handler-pass.
Нас в большинстве случаев интересует handler-fail, собсвенно действие в случае не правильного ввода, даем рекомендации юзеру, изменяем содержмое элемента.

Пользовательский обработчик это всегда пользовательская функция. Если подумать логично, то зачем реализовывать свой "урезанный" язык :)

Перед вызовом функции мы можем сконструировать необходимые аргументы. Нам дступны некоторые операции, например конкатенация строк и простейшие арифметические операции(+, -, / ,*). К сожалению реализовывать мы это будем на JS, следовательно код должен быть компактным. Сложные выражения без КС грамматик не реализовать, значит с ними будет туго. Но это не мешает нам внести сложные выражения в спецификацию.
Когда аргументы сформированны вызывается пользовательская функция. Отдельным аргументом к ней идёт контекст обработчика с информацией о элементе и т.п.
Для аргументов существует только один тип - строки. Языки не поддерживающие свободные функции(Java), реализуют свой механизм вызова(например для Java это будет некий интерфейс).

Пока выводы:
  • Мы разделили правила для пассивной и интерактвной проверки, в синтаксисе Описания это будет подобно псевдоклассам в CSS. Отсюда сложность с inline правилами, куда какие? smile Наиболее логичный дать каждому типу свой аттрибут: validator-passive, validator-interactive.
  • Обработчики(handlers) это всегда пользовательские функции. Мне нравится синтаксис привидённый мной топиком выше.
Что нужно: вспомнить свои ситуации с валидацией форм, какова была задача. Кидайте сюда описание, разберусь. На основе этих наблюдений будем составлять базовый набор правил.
ava
GoodBoy | 11.11.2004, 11:10 #
У меня обычно валидация сводится к таким типам проверок:
  • заполнены (или выбраны) ли поля вообще (скажем так: атрибут required)
  • исключить ввод недопустимых значений (к примеру букв там, где должны быть только цифры)
  • исключить ввод НЕ по шаблону (к примеру: дата - дд.мм.гггг или емэйл - что_то@что_то.что_то)
ava
Sardar | 11.11.2004, 15:33 #
Цитата (GoodBoy @ 11.11.2004, 10:10)
исключить ввод недопустимых значений (к примеру букв там, где должны быть только цифры)

Вообще это реализуется регами. Но не все обычные пользователи умеют составять реги, потому введём две опции:
  • allow-chars: charlist - допустимые символы, не в текстовых полях игнорируется. Charlist представляет собой список символов как в регах: [АБВ-Я] или имя.

  • disallow-chars: charlist - по аналогии выше не допустимые символы.
Опция работает как в интерактивном так и в пассивном режиме. Проверка выполняется первой, перед регами.
Цитата (GoodBoy @ 11.11.2004, 10:10)
исключить ввод НЕ по шаблону (к примеру: дата - дд.мм.гггг или емэйл - что_то@что_то.что_то)

Введём две опции, тоже существуущие только для текстовых полей:
  • allow-pattern: regexp - регулярное вырежение допускающее ввод. Regexp - регулярное выражение: /обычный синтаксис регов/ или имя. Синоиним: pattern

  • disallow-pattern: regexp - рег запрещающий ввод.
Заметим что патерны в основном нужны для пассивной проверки перед отправкой формы.

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

Цитата (GoodBoy @ 11.11.2004, 10:10)
заполнены (или выбраны) ли поля вообще (скажем так: атрибут required)

Обычно, что бы форма отправилась, все элементы должны пройти проверку удачно. Но преставим ситуацию, форма отправится если:
  • ни один чекбокс не отмечен, а текст заполнен правильно
  • тесктовое поле пусто или имеет некоторе спец. значение, при этом один или несколько чекбоксов отселектированны
  • в любых других сутуациях форма не отравляется.
Required, dependent-on и подобные здесь будут представлять частные случаи, потому не подходят. Пока идея, получилось немного обьемно:
HTML
<form name="test" validatorCommand="только если: texts xor checks">
<!-- только если текстовое поле или(исключающее) один или несколько из чекбоксов
если поменяем условие: texts xor cheks - то получим: если тесктовое поле или(исклучающее) (опция 2 и опция 3) -->
<input type="text" validator="pattern: email; value: none 'special value';" validatorCommands="or-group: texts"><br>
</input type="checkbox" validatorCommand="or-group: checks">Опция 1<br>
</input type="checkbox" validatorCommand="or-group: checks; and-group: cheks">Опция 2<br>
</input type="checkbox" validatorCommand="or-group: checks; and-group: cheks">Опция 3<br>
</form>

Текстовое поле допускает паттерн email(это просто имя, значение инклюдится) или значения: пусто, слова "special value". Только в этом случаем она перейдёт в состояние pass.
Чекбоксы добавляются в специальные группы, существующие только внутри конкретной формы. Из названия ясно как вычисляется результирующее значение. Теперь можно обратится к элементу индивидуально, либо к группе сразу.
Группа несёт значения своих элементов и состояние проверки(pass/fail). Что мы хотим использовать задаем синтаксисом:
  • имя_группы.value - значения, синоним: имя_группы
  • имя_группы.check - результат проверки
И вот проблема: как комбинировать группы? Можно ввести новуые команды для формы, которые шаг за шагом скомбинируют групы/элеметны либо придумать булевые выражения.
Естесвенно что комбинирование груп существует только для формы, не для элементов. Элементы могут только входить в одну или несколько груп. Группировка должна произойти так, что бы в конце осталось одно булево значение, которе и определит отрпавлятся ли форме.
Чувствую надо лучше продумать.

Добавлю: пример выше не верный, опция value несет другой смысл чем в задаче. Уже придумал немного другую концепций, позволяющую гибко работать с любым количеством различных проверок одного элемнта и комбинирования результатов. А главное понятно и довольно коротко smile
ava
Alx | 11.11.2004, 15:33 #
GoodBoy
согласен smile
ava
Alx | 11.11.2004, 16:42 #
так. пока я вот подумал, что нам надо будет проверять:

- заполнение необходимых полей
- верное заполнение e-mail
- верное заполнение определённых полей
- соответствие полей password1 и password2 (повтор пароля)
- в определённых селектах/чекбокс группах если ничего не выбрано, проверяем поле myValue (свой вариант) и наоборот. есливыбрат вариант из данных, свой : disabled и неиспользуется

дальше. думаю раз уж мы пишем такую фичу, можно сделать ещё немколько отдельных полей. Например:

<input type="country"> // option со всеми странами
<input type="сities" value="rus|ukr|eng|ger|fra|ita|usa"> (тут как раз должно быть поле "Или введите другой город", у value можно оставить только rus, если на твой взгляд не стоит морочится с другими городами.)
<input type="years" since="1900" for="2005"> // года с 1900 по 2005
<input type="mounthes"> // 12 месяцев
<input type="days1" value="31|30|29|28"> 30 дней в месяце, если используется с полем mounthes, нужно будет делать установку количества дней. Например: if(april) value = 30;
<input type="days2" value="s|m"> // дни недели (s - начало с воскресенья, m - начало с понедельника)

эти поля используются очень часто, поэтому сделать их на мой взгляд нужно. Тогда меньше будет возьни с их проверкой (если делать обыкновенные input`ы).
ava
Aliance | 16.11.2004, 18:49 #
У меня есть много собственных валидаторов, отличные штуки.
Итак, попорядку:


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!--
Автор: Лесных Илья
Специально для Vingrad :)
Copyright 2004 Aliance ©
-->
<html>
<head>
<title>Validators</title>
<script type="text/javascript">
// поле не пустое
function isNotEmpty(elem) {
var str = elem.value;
var re = /.+/;
if(!str.match(re)) {
alert("Нужно заполнить все обязательные поля.");
setTimeout("focusElement('" + elem.form.name + "', '" + elem.name + "')", 0);
return false;
} else {
return true;
}
}
// Пол. или отриц. число
function isNumber(elem) {
var str = elem.value;
var re = /^[-]?\d*\.?\d*$/;
str = str.toString();
if (!str.match(re)) {
alert("Только номера.");
setTimeout("focusElement('" + elem.form.name + "', '" + elem.name + "')", 0);
return false;
}
return true;
}
// Длина ровно 15 символов!!!
function isLen15(elem) {
var str = elem.value;
var re = /\b.{15}\b/;
if (!str.match(re)) {
alert("15 символов должно быть.");
setTimeout("focusElement('" + elem.form.name + "', '" + elem.name + "')", 0);
return false;
} else {
return true;
}
}
// Проверка на корректный e-mail
function isEMailAddr(elem) {
var str = elem.value;
var re = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/;
if (!str.match(re)) {
alert("Проверте адрес e-mail.");
setTimeout("focusElement('" + elem.form.name + "', '" + elem.name + "')", 0);
return false;
} else {
return true;
}
}
// Чекбаттон выбран
function isChosen(select) {
if (select.selectedIndex == 0) {
alert("Выберете чекбаттон.");
return false;
} else {
return true;
}
}

// Радиобаттон выбран
function isValidRadio(radio) {
var valid = false;
for (var i = 0; i < radio.length; i++) {
if (radio[i].checked) {
return true;
}
}
alert("Выберете радиобаттон.");
return false;
}

function focusElement(formName, elemName) {
var elem = document.forms[formName].elements[elemName];
elem.focus();
elem.select();
}

// функция самого валидатора
function validateForm(form) {
if (isNotEmpty(form.name1)) {
if (isNotEmpty(form.name2)) {
if (isNotEmpty(form.eMail)) {
if (isEMailAddr(form.eMail)) {
if (isChosen(form.continent)) {
if (isValidRadio(form.accept)) {
return true;
}
}
}
}
}
}
return false;
}
</script>
</head>
<body>
<form method="GET" action="login.php"
name="sampleForm" onsubmit="return validateForm(this)">
Ник: <input type="text" size="30" name="name1" id="name1"
onchange="isNotEmpty(this)" /><br>
Реальное имя: <input type="text" size="30" name="name2" id="name2"
onchange="isNotEmpty(this)" /><br>
E-mail: <input type="text" size="30" name="eMail" id="eMail"
onchange="if (isNotEmpty(this)) {isEMailAddr(this)}" /><br>
Регион: <select name="continent" id="continent">
<option value="" selected>выберите континент:</option>
<option value="Africa">Африка</option>
<option value="Asia">Азия</option>
<option value="Australia">Австралия/Океания</option>
<option value="Europe">Европа</option>
<option value="North America">С. Америка</option>
<option value="South America">Ю. Америка</option>
</select><br>
Принимаете соглашение:
<input type="radio" name="accept" id="accept1" value="agree" />Да
<input type="radio" name="accept" id="accept2" value="refuse" />Нет
<br>
<input type="reset" /> <input type="submit" />
</form>
</body>
</html>


Тут производяться 2 проверки: при изменении значения поля и при отсылки (общая) - какую выберете Вы - Вам решать :)

ЗЫ: что бы пользователь не обошел проверку на локальной машине я всегда делаю так:
Цитата


Вместо <input type="submit" />

Пишу <input type="button" onclick="this.form.submit();" />


В этом случае юзер, отключивший JS вообще не сможет отправить форму =)
später ergänzt:
ALEXANDRO
Первый раз вижу такие typeinput`ов...
Может это не type, а name???
ava
Sardar | 17.11.2004, 01:58 #
Aliance это код под определённую ситуацию, мы хотим сделать нечто общее. В данный момент обдумываем идею, реализовывать можно не только на JS, но и сделать встроенной в браузер. вообщем чтиай с начала ;-)

Цитата (Aliance @ 16.11.2004, 17:49)
Первый раз вижу такие type`ы input`ов...

Может это не type, а name???


Нет это "наши" типы, после загрузки они заменятются на конкретные элементы. У меня пока идея сделать для этого отдельный тег, дабы браузер не смущать.
ava
Alx | 17.11.2004, 09:31 #
я предлагаю назвать тег <alx> smile :p
ava
Aliance | 17.11.2004, 14:44 #
Сори, конечно должен я был все прочесть.
Просто понимаешь, меня 2 недели небыло, и прочесть все непрочитанные сообщения полностью за 1 день нереально smile
Я по теме смотрел ))
später ergänzt:
Тег нужно назвать <vin> (vingrad)
ava
Alx | 17.11.2004, 17:08 #
ну тогда уж его нужно назвать <spetialinput>
später ergänzt:
или что-то в этом роде....
Registrieren Sie sich oder melden Sie sich an, um schreiben zu können.
Unternehmen des Tages
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Mitwirkende
  Sardar   GoodBoy   Alx   sergejzr ava  Aliance
advanced
Absenden