Ненавязчивый JavaScript. Глава 5: Пример — форма, улучшенная JavaScript

Текст ниже — перевод статьи "Unobtrusive Javascript", чей автор Christian Heilmann любезно дал мне согласие на перевод. Оригинальный адрес статьи www.onlinetools.org/articles/unobtrusivejavascript/chapter5.html.

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

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

Формы и JavaScript — обманчивая мощь

JavaScript может сделать форму гораздо лучше, увеличив ее юзабилити, но мы должны осознавать несколько вещей:

  • Нам все же нужно делать проверку на стороне сервера, чтобы неверные данные — посланные пользователем с отключенным JavaScript — не достигали базы данных.
  • Не существует такой вещи как "автоматически сгенерированный скрипт проверки формы". Каждая форма исключительна и следует определенным правилам валидации и процесса перехода. Учет каждой возможности приведет к чрезмерно раздутому и медленному коду, способ лучше — это повторное использование отдельных частей библиотеки скрипта. Это дает более легкую поддержку и быстрое исполнение.
  • Попробуйте сохранять способы проверки одинаковыми, как на стороне клиента, так и на стороне сервера. Мы могли бы использовать классы для валидации на стороне клиента, например, <input class="required"/>, но они не отсылаются при отправлении формы.

Код нашей формы

<form action="formsend.php" method="post"
onsubmit="return checkform(this);">
  <p>
    <input type="hidden" name="required" id="required"
     value="name,surname,email,tac,msg,contactform" />
    <label for="name">Name</label>

    <input type="text" name="name" id="name" /><span>*</span>
  </p>
  <p>
    <label for="surname">Surname</label>

    <input type="text" name="surname" id="surname" /><span>*</span>
  </p>
  <p>
    <label for="email">Email</label>

    <input type="text" name="email" id="email" /><span>*</span>
  </p>
  <p>
    <label for="phone">Phone number</label>

    <input type="text" name="phone" id="phone" />
  </p>
  <p>
    <label for="contactform">Prefered form of contact</label>
    <select id="contactform" name="contactform">

      <option value="">Please choose</option>
      <option value="p">phone</option>
      <option value="e">email</option>
    </select><span>*</span>

  </p>
  <p>
    <label for="msg">Your message</label>
    <textarea name="msg" id="msg"></textarea><span>*</span>

  </p>
  <p>
    <input type="checkbox" name="tac" id="tac" />
	I have read the <label for="tac">terms and conditions</label>
	and agree with them.</label><span>*</span>

  </p>
  <p>
    <input type="submit" value="Send information" />
  </p>
</form>

Это отлично сверстанная форма, выполненная с метками, чтобы приспособить ее для не пользующихся мышью (non-visual) пользователей и тем пользователей, которым может быть трудно попасть в чекбокс их координатно-указательным устройством(мышью, трэкболом и т.д.). Для валидации, у нас есть скрытое поле называемое required (обязательные), оно содержит список всех обязательных для заполнения полей через запятую. Это стандарт для скриптов валидации формы (помните formmail.pl?) с незапамятных времен.

Правила, которым мы хотим следовать:

  • Убедитесь, что каждое обязательное поле заполнено, выбрано или отмечено.
  • Убедитесь, что email введен в правильной форме.

Большинство скриптов для валидации выводят список неправильно заполненных обязательных полей в предупреждение (alert) JavaScript. В этом есть смысл, если форма велика и сложна, но предупреждения раздражают и несколько отталкивают. Давайте попробуем для этой небольшой формы другой подход: каждое поле, в котором есть ошибка, должно получить небольшую иконку с предостерегающим знаком и красный цвет фона, а также мы покажем сообщение над кнопкой отправки формы (submit button), в котором говорится, что найдены ошибки.

Наш скрипт проверки формы ( checkform() )

Мы начинаем наш скрипт с тестирования, доступна ли DOM, и существует ли поле с ID required. Если не выполняется хотя бы одно из этих условий, мы возвращаемся к документу, и наш покой будет обеспечивать теперь PHP-скрипт обработки ошибок formsend.php.

function checkform(of)
{
  if(!document.getElementById || !document.createTextNode){return;}
  if(!document.getElementById('required')){return;}

Продолжаем, определяя все переменные, используемые в отображении ошибки, и разбивая строку — список ID обязательых полей в поле required — в массив.

  var errorID='errormsg';
  var errorClass='error'
  var errorMsg='Please enter or change the fields marked with a ';
  var errorImg='img/alert.gif';
  var errorAlt='Error';
  var errorTitle='This field has an error!';
  var reqfields=document.getElementById('required').value.split(',');

Раз мы добавляем элемент с определенным ID в errorID, а также изображения к каждому неправильно заполненному полю, нам нужно удалить все это, когда скрипт будет выполняться во второй раз. Иначе у нас будет несколько сообщений об ошибке и несколько изображений.

// Удаляем старые сообщения
  // если есть старое поле errormessage, удаляем его
  if(document.getElementById(errorID))
  {
    var em=document.getElementById(errorID);
    em.parentNode.removeChild(em);
  }
  // удаляем старые изображение и классы
  // в обязательных для заполнения полях
  for(var i=0;i<reqfields.length;i++)
  {
    var f=document.getElementById(reqfields[i]);
    if(!f){continue;}
    if(f.previousSibling && /img/i.test(f.previousSibling.nodeName))
    {
      f.parentNode.removeChild(f.previousSibling);
    }
    f.className='';
  }

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

// перебираем обязательыне поля
  for(var i=0;i<reqfields.length;i++)
  {
  // проверяем, существует ли это обязательное поле
    var f=document.getElementById(reqfields[i]);
    if(!f){continue;}

Потом мы проверяем каждое поле в соответствии с его типом. Для текстовых областей и полей мы должны проверить значение, для чекбоксов — отмеченный атрибут, для выпадающих меню — определен ли selectedIndex, и больше ли он 0.

Если в каком-то из полей есть ошибка, мы посылаем ее как объект методу cf_adderr(). Особый случай — это поле email, так как оно также нуждается в проверке на правильность электронного почтового адреса. Эта проверка исполняется другим методом, называемым cf_isEmailAddr(), в котором используются регулярные выражения.

// проверяем правильность заполнения обязательных полей
// в соответствии с их типом
    switch(f.type.toLowerCase())
    {
      case 'text':
        if(f.value=='' && f.id!='email'){cf_adderr(f)}
// email — особое поле и требует иной проверки
        if(f.id=='email' &&
        !cf_isEmailAddr(f.value)){cf_adderr(f)}
      break;
      case 'textarea':
        if(f.value==''){cf_adderr(f)}
      break;
      case 'checkbox':
        if(!f.checked){cf_adderr(f)}
      break;
      case 'select-one':
        if(!f.selectedIndex && f.selectedIndex==0){cf_adderr(f)}
      break;
    }
  }

Если какая-либо из проверок выше запускает отчет об ошибке, cf_adderr() создает сообщение об ошибке (DIV с errorId в качестве ID). Поэтому мы возвращаемся к процессу отправки формы, только когда этого элемента не существует.

return !document.getElementById(errorID);

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

Читать дальше