Przyszedł najwyższy czas na sprawdzenie, czy nie chcą nas oszukać :)
Sprawdzanie danych, które wprowadza użytkownik to jedna z ważniejszych rzeczy o jaką musimy zadbać przy tworzeniu strony.
Do sprawdzania pól formularza wykorzystamy metody String, wyrażenia regularne + trochę kawy do picia.
Weryfikacja danych może przebiegać w 3 etapach:
Bardzo ważną jest by przy projektowaniu formularzy odpowiedzieć sobie na pytanie "jakich pól będę wymagał". Zbyt duża liczba
wymagać sprawi, że użytkownicy po prostu ominą nasz formularz lub rejestrację.
W praktyce dla większości serwisów wystarczy email + hasło. Telefon, adres itp - wszystkie te dane powinny być tylko nie wymaganym dodatkiem.
Wykorzystajmy do naszych celów formularz z którego już korzystaliśmy:
<form id="Formularz" name="Formularz" method="post" action="skrypt_przetwarzajacy.php"> <fieldset> <legend>Pole</legend> <label for="Imie">Imię:</label><input type="text" name="Imie" id="Imie" value="" /> <label for="Mail">E-mail:</label><input type="text" name="Mail" id="Mail" value="" /> </fieldset> <fieldset> <legend>Wybierz płeć</legend> <input type="radio" value="m" name="plec" checked="checked" /> m <input type="radio" value="k" name="plec" /> k </fieldset> <fieldset> <legend>Wybierz dwa (2) ulubione owoce</legend> <input type="checkbox" value="jagoda" name="kolor" /> niebieski <input type="checkbox" value="jablko" name="kolor" /> czerwony <input type="checkbox" value="arbuz" name="kolor" /> zielony <input type="checkbox" value="pomarancz" name="kolor" /> pomarańcz </fieldset> <fieldset> <input type="submit" value="Zapisz" /> </fieldset> </form>
Wszystkie podpowiedzi i informacje o błędnych danych nie są treścią strony, więc nie powinny się znajdować w jej kodzie. Będziemy je tworzyć dynamicznie za pomocą metod createElemend, createTextNode itp.
Przechodzimy do pracy.
Pierwszy etap, który musimy obsłużyć polega na poinformowaniu użytkownika o tym, że dane pole zostało błędnie wypełnione.
W tej kwestii mamy nieograniczone możliwości. Możemy zmieniać kolor błędnego inputa (nadawać mu klasę z odpowiednim stylowaniem), tworzyć dodatkowy element z informacją itp.
W przykładzie poniżej przedstawiam najłatwiejsze podejście czyli nadanie odpowiedniej klasy:
function test() {
if (this.value != '') { //tutaj mamy zmieniony warunek
this.className = "error";
return false;
} else {
this.className = '';
return true;
}
}
var pole = document.getElementById('pole1');
pole.onchange = test;
Dla naszych celów zmieniliśmy warunek testu - zwracamy błąd jeżeli pole ma jakąś wartość.
Zastanawiasz się po co zwracamy w funkcji test() prawdę lub fałsz? Dzięki temu możemy taki test wykorzystać przy kolejnym etapie...
Dodanie nowego elementu jest nieco bardziej wymagające. JS standardowo nie posiada metody wysławiającej element za danym elementem. Musimy wiec napisać ją sami. Musimy także sprawdzić, czy wcześniej takiego elementu już nie stworzyliśmy.
Aby wstawić jakiś element za naszym wykorzystamy do tego metodę insertBefore.
W poniższych skryptach modyfikujemy obiekt Node (za pomocą instrukcji prototype), czyli każdy element na stronie.
Niestety starsze IE nie obsługują tej techniki, dlatego chcąc uruchamiać nasze skrypty także na tych przeglądarkach nasze metody będziemy
musieli przerobić na zwykłe globalne funkcje...
Node.prototype.insertAfter = function(newNode) {
if(this.nextSibling) { //jezeli dany element ma za soba jakis obiekt
return this.parentNode.insertBefore(newNode, this.nextSibling); //to wstawiamy przed tym obiektem nasz element
} else {
return this.parentNode.appendChild(newNode); //jeżeli nie ma za sobą obiektu, po prostu wrzucamy do rodzica ja koniec
}
}
//to samo dla starszych IE - będzemy przekazywać w 1 parametrze element za który wstawiamy
function insertAfterIE(element, newNode) {
if(element.nextSibling) { //jezeli dany element ma za soba jakis obiekt
return element.parentNode.insertBefore(newNode, element.nextSibling);
} else {
return element.parentNode.appendChild(newNode);
}
}
Dzięki powyższemu kodowi, rozszerzyliśmy wszystkie elementy na stronie o możliwość dodawania nowego elementu za sobą.
var pole = document.getElementById('pole1');
pole.onchange = function() {
if (this.value!='') {
//tworzymy nasz span
var spanError = document.createElement('span');
spanError.appendChild(document.createTextNode('zła wartość'));
spanError.className = 'pole-z-errorem';
this.insertAfter(spanError);
} else {
this.className = '';
}
}
Niestety w takiej konstrukcji tworzenie kolejnych podpowiedzi szybko zapełni nam naście ekranów. Dlatego przerobimy nasz kod na kolejną metodę:
Node.prototype.createError = function(msg) {
var spanError = document.createElement('span');
spanError.appendChild(document.createTextNode(msg));
spanError.className = 'pole-z-errorem';
this.insertAfter(spanError);
}
var pole = document.getElementById('pole1');
pole.onchange = function() {
if (this.value!='') {
this.createError('Błędna wartość');
} else {
this.className = '';
}
}
Nasza nowa metoda działa całkiem sprawnie, jednak nie sprawdza czy nasz span z komunikatem już istnieje.
Po drugiej, trzeciej próbie błędnego wypełnienia pola, będziemy mieli kilka komunikatów. Zajmijmy się więc i tym.
Wystarczy sprawdzić, czy następny element jest spanem z odpowiednią klasą. Przeglądarki różnie zwracają właściwość nodeName, więc zamieniamy ją na małe litery.
Node.prototype.createError = function(msg) {
if (this.nextSibling && (this.nextSibling.nodeName.toLowerCase()=='span' && this.nextSibling.className.indexOf('pole-z-errorem')!=-1)) return false;
var spanError = document.createElement('span');
spanError.appendChild(document.createTextNode(msg));
spanError.className = 'pole-z-errorem';
this.insertAfter(spanError);
}
Oczywiście powyższą metodę można jeszcze bardziej uelastycznić poprzez wyrzucenie sztywnej nazwy klasy:
Node.prototype.createError = function(msg, class) {
var class = class || 'pole-z-errorem'
if (this.nextSibling && (this.nextSibling.nodeName.toLowerCase()=='span' && this.nextSibling.className.indexOf(class)!=-1)) return false;
var spanError = document.createElement('span');
spanError.appendChild(document.createTextNode(msg));
spanError.className = class;
this.insertAfter(spanError);
}
Ostatnim krokiem jest usunięcie naszego komunikatu w przypadku, gdy dane pole zostanie poprawnie wypełnione.
Node.prototype.removeError = function(class) {
var class = class || 'pole-z-errorem'
if (this.nextSibling && (this.nextSibling.nodeName.toLowerCase()=='span' && this.nextSibling.className.indexOf(class)!=-1)) {
this.parentNode.removeChild(this.nextSibling);
}
}
Gotowy kod korzystający z powyższych metod:
Node.prototype.insertAfter = function(newNode) {
if(this.nextSibling) { //jezeli dany element ma za soba jakis obiekt
return this.parentNode.insertBefore(newNode, this.nextSibling); //to wstawiamy przed tym obiektem nasz element
} else {
return this.parentNode.appendChild(newNode); //inaczej
}
}
Node.prototype.createError = function(msg, class) {
var class = class || 'pole-z-errorem'
if (this.nextSibling && (this.nextSibling.nodeName.toLowerCase()=='span' && this.nextSibling.className.indexOf(class)!=-1)) return false;
var spanError = document.createElement('span');
spanError.appendChild(document.createTextNode(msg));
spanError.className = class;
this.insertAfter(spanError);
}
Node.prototype.removeError = function(class) {
var class = class || 'pole-z-errorem'
if (this.nextSibling && (this.nextSibling.nodeName.toLowerCase()=='span' && this.nextSibling.className.indexOf(class)!=-1)) {
this.parentNode.removeChild(this.nextSibling);
}
}
function test() {
if (this.value!='') {
this.createError('Błędna wartość');
return false;
} else {
this.removeError();
return true;
}
}
var poleA = document.getElementById('pole2a');
poleA.onchange = test;
var poleB = document.getElementById('pole2a');
poleB.onchange = test;
</script>
Jak widać z powyższego większość kodu to dopisanie funkcjonalności do javascript.
Aby uniknąć zbędnej pracy wystarczy skorzystać z którejś z gotowych bibliotek - np. jQuery (do czego gorąco zachęcam).
Zajmijmy się teraz obsługą danych podczas wysyłania.
Sprawdzenie danych wykonamy podczas zdarzenia onsubmit:
var formularz = document.getElementById('forma');
formularz.onsubmit = function() {
if (dane_bledne) {
return false; //nie wysyłamy
}
return true; //wysyłamy
}
Powyższa kod ukazuje jedynie sposób działania takiej metody. Przeprowadzenie sprawdzania danych zależy od tego, jak zaprojektujemy nasz skrypt.
Pierwszym ze sposobów jest wykorzystanie wcześniej napisanych funkcji sprawdzających:
formularz.onsubmit = function() {
var inputs = this.getElementsByTagName('input');
for (var i=0; i<inputs.length; i++) {
if (inputs[i].test()==false) {
//jeżeli któryś test zróci false oznacza to że dane są błędne
return false;
}
}
return true
}
W powyższym kodzie możemy także wykorzystać tablicę:
var test_results = [];
var inputs = this.getElementsByTagName('input');
for (var i=0; i<inputs.length; i++) {
test_results.push( inputs[i].test() );
}
if (test_results.indexOf(false)) {
return false;
}
Nie zawsze oczywiście będzie to takie proste. W końcu nasze formularze mają pola z mailami, radiobuttony, checkboxy itp, które też przydało by się sprawdzić.
W powyższym przykładzie metoda test ma bardzo uproszczony kod, który sprawdza tylko czy pola tekstowe są puste. W praktyce robiąc powyższą pętlę po inputach powinniśmy dodatkowo przeprowadzić testy rodzaju inputa:
for (i=0; i<inputs.length; i++) {
if (inputs[i].type == 'text) {
test_text_input(inputs[i]);
} else if (inputs[i].type == 'radio') {
test_radio_input(inputs[i]);
} else if (inputs[i].type == 'checkbox') {
test_checkbox_input(inputs[i]);
}
}
Ostatnim sposobem, który chciałem przedstawić, wykorzystuje dodatkowe klasy.
Każdemu polu, które jest wymagane dodajemy klasę wymagane.
Podczas indywidualnego sprawdzania (wyświetlania podpowiedzi) dodajemy takiemu polu odpowiednią klasę daneOK lub daneError.
Podczas wysyłania danych wykonujemy pętlę po wszystkich elementach formularza, sprawdzając czy dany element ma klasę wymagane.
Jeżeli element posiada taką klasę, sprawdzamy czy posiada też klasę daneError.
Gdy jest to prawdą, oznacza to, że dane w tym polu są niepoprawne. Aby dodawanie dodatkowych klas było usprawnione, zmodyfikujmy nasze wcześniejsze metody:
Node.prototype.createError = function(msg, class) {
var class = class || 'komunikat-z-errorem';
if (this.nextSibling && (this.nextSibling.nodeName.toLowerCase()=='span' && this.nextSibling.className.indexOf(class)!=-1)) return false;
var spanError = document.createElement('span');
spanError.appendChild(document.createTextNode(msg));
spanError.className = class;
this.insertAfter(spanError);
this.className.replace(/ daneOK/,'');
this.className+=" daneError";
}
Node.prototype.removeError = function() {
if (this.nextSibling && (this.nextSibling.nodeName.toLowerCase()=='span' && this.nextSibling.className.indexOf(class)!=-1)) {
this.parentNode.removeChild(this.nextSibling);
this.className.replace(/ daneError/,'');
this.className+=" daneOk";
}
}
Wystarczy teraz odpowiednio wykorzystać nasze metody:
//sprawdza zwykłe pole tekstowe
function sprawdzPole() {
if (this.value=='') {
this.createError('Wpisałeś złe dane');
return false;
} else {
this.removeError();
return true;
}
}
//funkcja sprawdza poprawnosc maila
function sprawdzMaila() {
var WzorMaila = /^[0-9a-z_.-]+@[0-9a-z.-]+\.[a-z]{2,3}$/i
if (!WzorMaila.test(this.value)) {
this.createError('Podałeś złego emaila');
return false;
} else {
this.removeError();
return true;
}
}
//etap 1 - podpowiedzi
var formularz = document.getElementById('forma');
formularz['Imie'].onchange = sprawdzPole;
formularz['Mail'].onchange = sprawdzMaila;
//etap 2 - wysylanie
formularz.onsubmit = function() {
for (x=0; x<this.forms.elements.length; x++) {
if (this.className.indexOf('wymagane')!=-1 && this.className.indexOf('daneError')!=-1) {
//błędne dane - nie wysyłamy
return false;
}
}
return true;
}
Na zakończenie warto zapoznać się też z gotowymi bibliotekami służącymi do walidacji: