Блогът на Гонзо

Валидиране на форми в браузъра

В тази статия ще се опитам да ви покажа как лесно да направите валидация на форми с HTML5. Не че няма сигурно хиляди модули за всяка възможна библиотека или фреймуърк, обаче те ще ви наложат техния интерфейс и начин на задаване на ограниченията върху полетата, а това може и да не е по вкуса ви. Често пъти за да са съвместими с по-старите браузъри такива модули са доста тежки.

Всъщност като за начало не е необходимо да правите почти нищо, достатъчно е да използвате подходящи типове на полетата, като например type=“email“ за полетата за имейл, type=“numeric“ за числовите полета и т.н. Добавете атрибута required за задължителните полета и готово. Ако имате по-специални изисквания за формата на някое поле, можете да използвате атрибута pattern, в който да зададете подходящ регулярен израз. За телефон например можете да използвате нещо такова: pattern=“(+|00?)?[\d\s-\/()#]+“ – телефонът трябва да започва с + или една или две нули и да съдържа цифри, евентуално интервал, скоби или #. Да видим пример:

<div>
  <label for="name">Име*</label>
  <input id="name" name="name" required="required" type="text" />
</div>
<div>
  <label for="email">Имейл*</label>
  <input id="email" name="email" required="required" type="email" />
</div>
<div>
  <label for="phone">Телефон*</label>
  <input id="phone" name="phone" pattern="(+|00?)?[аs-/()#]+" required="required" type="tel" />
</div>
<div>
  <button type="submit">Изпрати</button>
</div>

Тази форма ще работи почти идеално – няма да можете да я изпратите ако данните са невалидни, потребителят ще получи обратна връзка от браузъра с индикация какво не е попълнено правилно… но! Видът на съобщенията може да не е точно желания от Вас, текстът най-вероятно ще е доста лаконичен и при това на език, различен от езика на сайта. И трите проблема са лесно решими със съвсем малко CSS и JavaScript. Нека първо се опитаме да оформим по-добре полетата с малко CSS:

input {
  border: 1px solid #999;
  border-radius: .25em;
}

input:invalid {
  border-color: #900;
  box-shadow: none;
}

input:valid {
  border-color: #090;
}

Замисълът на този CSS е прост – оформили сме основния вид на полетата и чрез псевдокласовете :invalid и :valid задаваме различен цвят на контура ако полето не е попълнено правилно или ако е. Ако го сложите обаче в сайта си, веднага ще забележите проблем – задължителните полета веднага ще добият червен контур, защото преди да ги попълни потребителя те са невалидни. За да избегнем това ще „активираме“ псевдокласовете след като потребителя вече е взаимодействал с полето. Просто ще добавим един клас на полето когато потребителят го напусне и ще променим стиловете така, че да важат само за комбинацията от този клас и състоянието на полето:

.interacted:invalid {
  border-color: #900;
}

.interacted:valid {
  border-color: #090;
}
var inputs = document.querySelectorAll('input');
inputs.forEach(function(input, i, array){
  input.addEventListener('blur', function(e){
    input.classList.add('interacted');
  }, true);
});

Една забележка: тук и по-надолу съм използвал forEach върху nodeList, а това е метод на Array, който не е достъпен за nodeList. Най-лесното, но силно недолюбвано решение е:

NodeList.prototype.forEach = Array.prototype.forEach;

В демонстрацията, която подготвих, използвах един не толкова удобен, но по-безопасен вариант:

var forEach = function(ctn, callback){
  return Array.prototype.forEach.call(ctn, callback);
}

И така, решихме проблема с вида на полето когато е невалидно и обратно, когато е валидно. Обаче ако езикът на браузъра не е български, и съобщенията, които браузъра показва за невалидните полета, няма да са на български. На мен ми хрумна решение на този проблем (а то се оказа и документирано в MDN). Свойството validity на DOM елемента е обект, чиито свойства показват кои от ограниченията на стойността на полето не са изпълнени. Ако проверим стойностите, можем чрез метода setCustomValidity() да зададем собствен текст на съобщението за грешка. В следващия код съобщението отразява само една от грешките, но за вас няма да е трудно да го промените да показва всички грешки в едно съобщение.

function setValidationMessage(el){
  var validity = el.validity;
  if( validity.valid ) {
    el.setCustomValidity('');
    return;
  }
  if( validity.valueMissing ){
    el.setCustomValidity('Полето е задължително, моля, въведете стойност.')
  }
  if( validity.typeMismatch ){
    el.setCustomValidity('Въвели сте невалидна стойност.')
  }
  if( validity.patternMismatch ){
    el.setCustomValidity('Въведената стойност не отговаря на формата на полето.')
  }
  if( validity.rangeOverflow ){
    el.setCustomValidity('Въведената стойност е по-голяма от максималната позволена.')
  }
  if( validity.rangeUnderflow ){
    el.setCustomValidity('Въведената стойност е по-малка от минималната позволена.')
  }
  if( validity.tooLong ){
    el.setCustomValidity('Въведеният текст е твърде дълъг.')
  }
}

inputs.forEach(function(input, i, array){
  input.addEventListener('blur', function(e){
    this.setCustomValidity('');
    setValidationMessage(this);
  }, true);
});

Сега вече съобщенията са на български и потребителите са доволни, че разбират какъв е проблема. Но какво да направим с вида на съобщенията? Тъй като няма начин да оформим вградените в браузъра балончета, ще ги избегнем изцяло и ще показваме съобщенията сами. За нуждите на тази статия ще сложа текста под полето в един параграф. И когато съобщението е невалидно, в параграфа ще слагам стойността на свойството validationMessage, която след предишната интервенция вече съдържа обяснителен текст на български. А за да не се показват балончетата на браузъра просто ще извикаме метода preventDefault() на събитието invalid.

<div>
  <label for="email">Имейл*</label>
  <input id="email" name="email" required="required" type="email" />
  <p></p>
</div>
inputs.forEach(function(input, i, array){
  var p = this.parentNode.getElementsByTagName('p')[0];
  input.addEventListener('invalid', function(e){
    e.preventDefault();
  }, true);
  input.addEventListener('blur', function(e){
    if(this.checkValidity()){
      p.innerHTML = '';
    }
    else {
      p.innerHTML = this.validationMessage;
    }
  }, true);
});

That’s all, folks! Вече имаме наша си валидация – можем да я оформим както си искаме, да сложим каквито искаме съобщения, но не се налага да измисляме топлата вода по отношение на същинското валидиране на стойностите.

– А к’во пра’им със старите браузъри?

Ами ш’ги китнем малко да запълним дупката, напра’о ш’ги тунинговаме. Първоначално си опитах да използвам H5F на Ryan Seddon, но нещо не сработи добре и като не си намерих грешката реших да опитам нещо друго. След кратко търсене намерих webshims на Alexander Farkas, който китна идеално IE9 и освен това добави и други неща от които имах нужда по-късно, като например URLUtils.

Естествено, за старите браузъри има и друг вариант – да не правите нищо от страна на клиента. Така или иначе валидацията на сървъра ще върне потребителя обратно с подробно обяснение на грешките.

– А к’во пра’им ако няма JavaScript?

Има разни причини, поради които вашия скрипт може да не сработи при някой клиент – може потребителя да е изключил въобще JavaScript, може бъг във вашия скрипт или в браузъра да предизвика грешка, може поради проблеми в мрежата скриптът въобще да не се изтегли или да бъде блокиран от прекалено рестриктивна защитна стена. За да сме сигурни, че ще имаме основна функционалност и без JS можем да използваме пра-стария трик с клас на елемента body или html, който бива подменен от JS, и на базата на него да нагласим стиловете да работят по подходящ начин.

Ако искате да видите горните неща в действие, на тази демонстрационна страница има няколко форми, върху които са приложени отделните стъпки от статията.

Етикети: , , ,

Вашият коментар

Вашият имейл адрес няма да бъде публикуван. Задължителните полета са отбелязани с *