Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Управляемый скриптом ввод в поля по шаблону.


.
краткое содержание

Управляемый скриптом ввод в поля по шаблону.

Система управляемого ввода данных пользователем. Скрипт следит за каждым вводимым символом и подсказывает в случае ошибки и отклонения от шаблона. Вводит автоматически предусматриваемые символы.
Существует дней: 387
Автор: 12345c
Другие выпуски:
Рассылка 'Упражнения по яванскому письму. Javascript.'
 

12.05.2007

Добавить комментарий

Две новости, полезных для рассылки.

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

Обсуждение статьи - на vingrad.ru или в комментариях на этой странице.
Ссылка на код библиотеки Javascript - (operatedInput.zip (48Kба )).
Пример к статье - ввод телефонного номера.

 

Что такое управляемый ввод?

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

 

Постановка задачи

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

Итак:

  1. Система должна выполнять свою основную функцию, т.е. не давать пользователю ввести неверную (не соответствующую маске) информацию.
  2. Система должна, каким-то образом сообщать пользователю о своей работе, чтобы у него не сложилось впечатление, что "в этот input ничего ввести нельзя, сайт не работает".
  3. Мы хотим получить систему, которую можно использовать не один раз, а сколько угодно, без какого-либо её изменения. Также мы хотим использовать её на уже имеющихся HTML формах, не внося в эти уже готовые и работающие формы корректировок.

Т.е. иными словами, система должна соответствовать архитектуре "Модель-представление-контроллер". Это звучит сложно и внушительно, однако все достаточно просто.

В нашем случае представление - это страница вместе с размещенной на ней формой. Модель – это логика, принимающая решение относительно соответствия вводимого символа маске. И, как мы уже договорились, логика не должна быть смешана с конкретным кодом HTML станицы. Или другими словами: модель отделена от представления.

Контроллер носит функцию связующего звена между моделью и представлением. Таким связующим звеном будут уникальные идентификаторы полей ввода ID.

 

Рис. 1 Представление и модель взаимодействуют только через контроллер.

4. Система должна быть кроссбраузерной. Всем угодить нельзя, поэтому остановимся на 3-х основных браузерах: Internet Explorer, Mozilla FireFox и Opera. (система тестировалась на IE6, Opera 9.10, FF 2.0.0.3)

 

Оповещение пользователя

Начнем именно с этого.

Очень часто можно встретить, что пользователь извещается о каких-нибудь незначительных событиях так называемым alert-ом. Окном с единственной кнопкой "OK". Это окно мало того, что появляется под устрашающий аккомпанемент звука системной ошибки, оно еще и блокирует весь интерфейс пользователя. Скорее всего, неискушенный человек поскорее закроет такой сайт, искушенный будет отвлекаться от своей прямой задачи (поиска информации, подписки на рассылку и т.п.). В идеале пользователь не должен прерывать последовательность своих действий, не должен отвлекаться на процессы, не связанные напрямую с его работой. Это рассеивает его внимание, снижает производительность труда, делает работу с приложением неудобной.

Мы сделаем по-другому. Пусть в случае неверного ввода окошко ввода будет обрамляться красной рамкой, или, скажем, цвет фона в нем будет меняться на синий. Или пусть это окошко вытянется на всю страницу. Неважно, мы всегда сможем легко изменить эффект выделения потом, даже не открывая сценарий JavaScript. Как!? Помните “Модель-представление-контроллер”?

Мы не станем менять оформление окошка ввода из JavaScript, мы просто присвоим этому окошку стиль CSS.

Например, такой:

 .text_error{
 border: 1px solid #FF0000;
}

Само по себе выделение уже достаточно информативно – пользователь легко заметит, что сделал что-то не так (например, пытается ввести свое имя взамен телефона). Но пусть для пущей надежности под окошком ввода будет появляться подсказка.

Таким образом, функция, отвечающая за информирование пользователя должна получить:

  1. Уникальный ID поля, требующего выделение - elementID
  2. Текст подсказки пользователю - HTMLtex. Такое название было дано, чтобы подчеркнуть возможность передать функции не только текст, но и HTML-разметку.
function mark2(elementID, HTMLtex){
 var target = document.getElementById(elementID);
 target.className='text_error'; //присвоение стиля CSS
 target.insertAdjacentHTML('afterEnd', '
'+HTMLtex); }

Сразу за полем ввода, будет добавлен текст. Очень просто. Все бы ничего, но данный код не работает в FireFox.

Вот тут мы столкнулись с проблемой известной всем web разработчикам – кроссбраузерность.

Эта проблема может быть решена, при том решена так, что нам даже не придется вносить изменение в function mark2. Здесь нам на помощь приходит образ разработки Adapter. Не сам, конечно, а в образе небольшого JavaScript-скрипта, разработанного с использованием данного образа. Т.е. недостающие возможности (insertAdjacentHTML) будут эмулированы для FireFox, таким образом, что мы сможем обращаться к insertAdjacentHTML, как будто бы этот браузер поддерживает данный метод. Применим скрипт разработанный Thor Larholm, просто подключим скрипт к странице. (Скрипт находится в приложении к статье.)

 

Следующее, что нам понадобится — удалять подсказку и выделение окошка ввода. Таким образом, функция должна получить:

1. Уникальный ID поля, требующего отмены выделения – elementID.

Мы легко сможем отменить выделение, отменяя примененный ранее стиль CSS:

var target = document.getElementById(elementID);
target.className=''; //присвоение стиля CSS

2. Но как быть с текстом подсказки? Сейчас это просто текст, и отличить его от любого другого текста на странице мы никак не можем. Изменим function mark2.

function mark2(elementID, HTMLtex){
 var target = document.getElementById(elementID);
 var boxID = 'err_'+elementID;
 target.className='text_error';
 target.insertAdjacentHTML('afterEnd', '<span
   id='+boxID+'><br/>'+HTMLtex+ '</span>');
}

Теперь подсказка выводится в контейнере SPAN c ID сгенерированным function mark2. Этот контейнер мы и будем удалять.

function unmark2(elementID){
 var boxID = 'err_'+elementID;
   //сгенерируем ID контейнера подсказки
 if (document.getElementById(boxID) != null){
  var box = document.getElementById(boxID);
  box.parentNode.removeChild(box);
  document.getElementById(elementID).className='';
 }
}

Ну вот и все. Но внимательный читатель наверняка заметил допущенную ошибку. Как быть если function mark2 будет вызвана для одного и того же поля несколько раз подряд? Будет создаваться несколько контейнеров с одним и тем же ID. Это недопустимо. Пусть function mark2 проверяет наличие текста подсказки, и при необходимости удаляет её вызывая function unmark2.

function mark2(elementID, HTMLtex){
 var target = document.getElementById(elementID);
 var boxID = 'err_'+elementID;
 if (document.getElementById(boxID) != null)
   unmark2(elementID);
 target.className='text_error';
 target.insertAdjacentHTML('afterEnd', '<span
   id='+boxID+'><br/>'+HTMLtex+ '</span>');
}

 

Назначение и выбор события

Сами по себе функции абсолютно бесполезны, их нужно как-то вызывать. function mark2 и function unmark2 являются вспомогательными, и будут вызываться другой функцией. Но кто вызовет эту другую функцию? Сам пользователь, посредством механизма событий. В арсенале JavaScript есть множество событий: onclick, onload и т.д.

 

ПРИМЕЧАНИЕ: именно onclick, onload, а не onСlick, onLoad. Это кстати весьма распространенная ошибка.

 

О выборе события мы поговорим позже. Сейчас нужно определить, каким образом привязать событие к объекту DOM – окошку ввода. Первое что приходит на ум - это указать событие прямо в HTML-дескрипторе. Что-то типа такого:

<input type="text" id="inp" onkeypress="funk();"/>

Нас такой вариант не устраивает. Система должна быть легко применима к уже готовым формам, кроме того, система должна быть как можно менее привязана к HTML-разметке. Да и HTML-разметка тоже не должна зависеть от системы. Опять вспоминаем "Модель-представление-контроллер". Достаточно только указать ID.

<input type="text" id="inp"/>

Теперь и верстальщик, и дизайнер смогут творить, не отвлекаясь на загадочные onkeypress, onclick.

Ссылку же на объект, а вместе с ним и на событие, будем получать прямо из JavaScript скрипта.

var input = document.getElementById(inputID);
input.onkeypress = check;

Где check() – это функция JavaScript, которую мы напишем позже.

Этот код необходимо поместить в обработчик window.onload. (Или в любой другой, в зависимости от конкретного случая.)

 

ПРИМЕЧАНИЕ: Стоит отметить, что при связывании функции (здесь check()) с событием (onkeypress) не нужно указывать круглые скобки. Следующая запись неверна:

input.onkeypress = check();

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

Правильная запись:

input.onkeypress = check;

В этом случаи функция check() присваивается элементу DOM (окошку ввода), сообщая ему о том, что должна быть вызвана нажатием клавиши.

 

При выборе события у нас есть несколько альтернатив:

onkeydown - нажатие и держание клавиши

onkeypress – нажатие клавиши

onkeyup - отпускании клавиши

Наиболее простым решением было бы использовать onkeyup, так как к моменту срабатывания этого события символ уже есть в поле ввода. Его можно оттуда прочитать, проанализировать и, если он не удовлетворяет условиям, удалить. И такой вариант часто применяется; он хорош, когда нужно, например, ограничить количество вводимых символов. Получается даже интересный эффект: символы появляются, а потом можно видеть, как они исчезают. Однако, до того как символ сотрется можно успеть набрать еще три других. Если при каждом onkeyup высчитывается длина строки в поле ввода, а потом «излишки» отрезаются – в результате, конечно, мы получим строку нужной длинны. Но для нас это недопустимо. Представьте: пользователь быстро вводит текст. События onkeyup перекрывают одно другое (уж не знаю, как это происходит), и в результате проанализированным окажется только последний символ.

Значит, нам нужно перехватывать и анализировать символ ещё до того, как он будет введен в окошко ввода. Этому требованию отвечает только onkeypress.

Вот что у нас получилось:

var input = document.getElementById(inputID);
input.onkeypress = check;

function check(){
_ действия _
}

Для удобства дальнейшего применения «обернем» все в одну функцию:

function operatedInput(inputID){
 var input = document.getElementById(inputID);
 input.onkeypress = check;

function check(){
_ действия _
}
}

Теперь, чтобы назначить полю ввода проверку нужно написать одну строчку, а не две:

operatedInput("input1");

 

Разработка алгоритма

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

Давайте прикинем, что у нас есть:

  1. Мы можем перехватить нажатие клавиши.
  2. Мы можем выяснить код вводимого символа, но не сам символ (это связано с выбором события onkeypress, т.к. сам символ еще не введен).
  3. К этому символу мы должны применить правило. Например, перевести символ в верхний регистр, или запретить его ввод.

Где хранить это правило (признак правила)? Пойдем проверенным, если не единственным, путем. Пусть признак правила хранится в символе маски. Например:

">" - будет означать, что вводимый символ должен быть в верхнем регистре;

"`" – (этот символ можно получить нажатием клавиши "ё", при латинской раскладке) что на этом месте должна быть цифра;

 

Тогда, для примера, маска для ввода номера телефона будет выглядеть так:

(```)```-``-``

(095)345-06-76 или (000)111-22-33

Символы, используемые как признак правила, назовем специальными. Вот небольшая, составленная не по ГОСТу (да простит меня читатель) структурная схема:

 

Рис. 2 Примерный вид структурной схемы, реакции на событие onkeypress.

 

Реализация алгоритма

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

Начнем с реализации применения правил.

var rule = mask.charAt(Rnamber); //извлекаем символ по номеру
switch (rule){
 case '_':  //соответствует любому символу
  return true;
  break;

 case '`':  //только цифра
  if ((code < 48) || (code > 57)){return false;}
  else{return true;}
  break;
  
 case '~':  //только русская буква
  if ((code < 1040) || (code > 1103)){return false;}
  else{return true;}
  break;
  
 case '':  //соответствует концу маски
  return false;
  break;
  
 default:
  input.value += rule;
  return true;
  break;
}

rule – символ маски

Rnamber – номер символа маски по порядку

code – код вводимого символа

input – ссылка на поле ввода

Получаем символ маски и проверяем, согласовано ли с ним какое-нибудь правило (является ли он специальным). В данном примере предусмотрено только 4 правила:

- в данной позиции должна стоять только цифра;

- только русская буква;

- в данной позиции может быть любой символ;

- достигнут конец маски, вводить больше символов нельзя.

Мы проверяем, соответствует ли вводимый символ правилу, и если нет, возвращаем false.

Если символ маски не специальный (default) мы выводим его и разрешаем ввод символа пользователя. Стоп! Мы же должны разрешить вводить символ пользователя только после применения правила (проверки) — см. структурную схему. Т.е. нужно взять следующий символ маски и опять проделать те же операции. И так — до тех пор, пока нам не встретится специальный символ. Для этого воспользуемся рекурсией – «обернем» операции в функцию, и пусть она вызывает сама себя.(даже и не знаю почему я выбрал рекурсию — существуют способы добиться требуемого эффекта куда более элегантным способом)

.
var bool = false; //флаг ошибки ввода
function doRule(Rnamber){
var rule = mask.charAt(Rnamber); //переменна rule содержит правило
 switch (rule){
 case '_':
  return true;
  break;
 case '`':
  if ((code < 48) || (code > 57)){
   mark2(inputID,'ожидается цифра');
   bool = true;
   return false;
  }
  else{
   unmark2(inputID);
   bool = false;
   return true;
  }
  break;
 case '~':
  if ((code < 1040) || (code > 1103)){
   mark2(inputID,'ожидается русская буква');
   bool = true;
   return false;
  }
  else{
   unmark2(inputID);
   bool = false;
   return true;
  }
  break;
 case '':
  return false;
  break;
 default:
  input.value += rule;
  doRule(Rnamber+1);
  if(bool){return false;}
  else{return true;}
     break;
 }
}

Еще мы добавили информирование пользователя – те самые mark2() и unmark2().

Все замечательно, но в Opera этот код будет работать некорректно.

input.value += rule;

Здесь мы заменяем содержимое окошка ввода его же содержимом с добавленным символом rule. Можно записать по другому:

input.value = input.value + rule;

После такой операции в Opera курсор ввода ставится в начало строки. Получается, что символы добавляются не в конец, а в начало. Принудительно передвинем курсор в конец:

//ставим курсор в конец для Opera
if(input.setSelectionRange) {
var end = input.value.length;
input.setSelectionRange(end,end);
}

setSelectionRange недоступен в IE, и без проверки if(input. setSelectionRange) наша функция перестала бы в нем работать.

 

ПРИМЕЧАНИЕ: здесь мы применили метод обнаружения объектов. Т.е. непосредственно обратились к объекту и узнали, доступен ли он. Это лучше, чем выяснять версию браузера и подстраивать код под конкретный его тип.

Метод обнаружения объектов более надежен, может работать в более поздних версиях браузеров, а также в браузерах, наличие которых мы не учли.

 

Вот так будет выглядеть ассоциативный default-у оператор:

default:
input.value += rule;
//ставим курсор в конец для Opera
if(input.setSelectionRange) {
 var end = input.value.length;
 input.setSelectionRange(end,end);
}
doRule(Rnamber+1);
if(bool){return false;}
else{return true;}
break;

 

Следующим шагом будет получение кода вводимого символа.

Помните, мы назначали событию onkeypress выполнение функции check(). Функция check() может получить только один параметр: объект Event (у нас для краткости e). IE и Opera передают его по-разному, следующий код учитывает этот факт:

var evt = (e) ? e : window.event;

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

var code = (document.all) ? evt.keyCode:evt.charCode;

Для IE или Opera это будет keyCode, для других charCode.

Также необходимо учесть, что событие onkeypress не обязательно означает, что пользователь хочет ввести символ. Он может хотеть его стереть – нажимать клавишу Backspace.

if ((code == 0) || (code == 8)){return true;}

Для FF и IE – 0, для Opera 8. (Я не знаю, что соответствует коду символа равного 8 для FF и IE (наверно какой-нибудь непечатный символ), но все равно, пусть будет :) )

Соберем все куски воедино:

function operatedInput(inputID,mask){
 var input = document.getElementById(inputID);
 input.onkeypress = check;
 function check(e){
  var evt = (e) ? e : window.event;
  var code = (document.all) ? evt.keyCode:evt.charCode;
  if ((code == 0) || (code == 8)){return true;}
  //получение правила для символа
  var bool = false; //флаг ошибки ввода
  function doRule(Rnamber){ 
  
  ... код вырезан для экономии места... 
  
  }
  var namber = input.value.length;
  return doRule(namber);
 }
}

В приложении к статье вы найдете готовую работающую библиотеку (operatedInput.zip (48Kба )).

 

Пример использования

На отдельной странице приведён работающий пример использования скрипта.

Задание маски:

Используйте следующие специальные символы:

"`" – на месте этого символа должна быть цифра (этот символ можно получить нажатием клавиши "ё", при латинской раскладке);

"~" – только русская буква;

"_"- любой символ;

Например, маска для ввода телефона будет выглядеть так:

var mask = "(```)```-``-``";

Назначение проверки:

Разместите на странице поле ввода с уникальным ID:

<input name="Name" type="text" value="" id="inp1">

В обработчик window.onload (или в любой другой, в зависимости от конкретного случая) добавьте строку:

<script type="text/javascript">
 window.onload=function(){
  operatedInput(' unp1','(`~__)`__)'; //назначение проверки
 }
</script>

В CSS страницы включить (для индикации ошибки ввода):

 .text_error{
 border: 1px solid #FF0000;
}

 

ToDo list

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

Также следует доработать механизм оповещения, а именно выводить подсказки не в текст страницы, а в слое поверх основного. Напомним, обсуждение и предложения по скрипту ведутся на vingrad.ru или в комментариях.

Дата написания - 30.04.2007.
Дата написания - Ключевые слова: library, script, subs027, для всех, статья.
Автор: AlexB

@->
 javascript.aho.ru , © I.Svetlov, 2005-2006 
Текущая очерёдность плана статей (подписчики могут корректировать через голосование).
11. Анимация падающего снега в окне браузера.
10. Инструменты Web 2.0 - приложения, работающие через браузер.
9. Многуровневое меню с навигацией по наведению мыши.
8. Ключевые слова новых технологий, которые нужно знать разработчику веб-страниц.
3. Как писать тексты с доступом через JS без экранирования специальных символов (&lt; и другие).
4. select и list - в них есть много общего. Как и с меню навигации. Эмулятор селекта.
5. Древовидное меню, подход к данным, отделение данных от представления.
6. Многонедельный календарь со ссылками. (По списку строится календарь.)

Форум сайта рассылки, почта автора рассылки.

 


В избранное