Dobro dоšli na drugi deo JavaScript tutorijala u kom ćemo nastaviti sa našim primerom validacije Web forme. U prvom delu sam objasnio kako naša skripta funkcioniše, uz napomenu da joj nedostaju određena poboljšanja kako bi bila sasvim funkcionalna i u skladu sa današnjim web standardima. Na primer, u ovom nastavku ćemo dodati funkcije za proveru:
1. da je korisnik čekirao select i radio dugmeta – ovo je sasvim opciono i možda vama ne treba forma koja ima i radio proveru u JavaScript-u, ali u ovom tutorialu će i to biti pokriveno.
2. da li je korisnik u dva input polja vezana za mail (mail i ponovljeni mail) ukucao istu email adresu
3. da li je email validan – da li sadrži određene karaktere za koje nije tipično da se javljaju u mail adresi, ili da li je korisnik ukucao @ npr.
Tako da će naša skripta na kraju izgledati ovako:
window.onload = initForms;
function initForms() {
for (var i=0; i< document.forms.length; i++) {
document.forms[i].onsubmit = function() {return validForm();}
}
}
function validForm() {
var allGood = true;
var allTags = document.getElementsByTagName("*");
for (var i=0; i<allTags.length; i++) {
if (!validTag(allTags[i])) {
allGood = false;
}
}
return allGood;
function validTag(thisTag) {
var outClass = "";
var allClasses = thisTag.className.split(" ");
for (var j=0; j<allClasses.length; j++) {
outClass += validBasedOnClass(allClasses[j]) + " ";
}
thisTag.className = outClass;
if (outClass.indexOf("invalid") > -1) {
invalidLabel(thisTag.parentNode);
thisTag.focus();
if (thisTag.nodeName == "INPUT") {
thisTag.select();
}
return false;
}
return true;
function validBasedOnClass(thisClass) {
var classBack = "";
switch(thisClass) {
case "":
case "invalid":
break;
case "reqd":
if (allGood && thisTag.value == "") {
classBack = "invalid ";
}
classBack += thisClass;
break;
case "radio":
if (allGood && !radioPicked(thisTag.name)) {
classBack = "invalid ";
}
classBack += thisClass;
break;
case "email":
if (allGood && !validEmail(thisTag.value)) {
classBack = "invalid ";
}
classBack += thisClass;
break;
default:
if (allGood && !crossCheck(thisTag,thisClass)) {
classBack = "invalid ";
}
classBack += thisClass;
}
return classBack;
}
function crossCheck(inTag,otherFieldID) {
if (!document.getElementById(otherFieldID)) {
return false;
}
return (inTag.value == document.getElementById(otherFieldID).value);
}
function radioPicked(radioName) {
var radioSet = "";
for (var k=0; k<document.forms.length; k++) {
if (!radioSet) {
radioSet = document.forms[k][radioName];
}
}
if (!radioSet) return false;
for (k=0; k<radioSet.length; k++) {
if (radioSet[k].checked) {
return true;
}
}
return false;
}
function validEmail(email) {
var invalidChars = " /:,;";
if (email == "") {
return false;
}
for (var k=0; k<invalidChars.length; k++) {
var badChar = invalidChars.charAt(k);
if (email.indexOf(badChar) > -1) {
return false;
}
}
var atPos = email.indexOf("@",1);
if (atPos == -1) {
return false;
}
if (email.indexOf("@",atPos+1) != -1) {
return false;
}
var periodPos = email.indexOf(".",atPos);
if (periodPos == -1) {
return false;
}
return true;
}
function invalidLabel(parentTag) {
if (parentTag.nodeName == "LABEL") {
parentTag.className += " invalid";
}
}
}
}
Krenimo redom! Kao što se primećuje, funkcija validBasedOnClass(thisClass) je dopunjena i sada omogućava još dve nove situacije: radio ili email klase.
function validBasedOnClass(thisClass) {
var classBack = "";
switch(thisClass) {
case "":
case "invalid":
break;
case "reqd":
if (allGood && thisTag.value == "") {
classBack = "invalid ";
}
classBack += thisClass;
break;
case "radio":
if (allGood && !radioPicked(thisTag.name)) {
classBack = "invalid ";
}
classBack += thisClass;
break;
case "email":
if (allGood && !validEmail(thisTag.value)) {
classBack = "invalid ";
}
classBack += thisClass;
break;
default:
if (allGood && !crossCheck(thisTag,thisClass)) {
classBack = "invalid ";
}
classBack += thisClass;
}
return classBack;
}
Ukoliko je kao parametar (naziv klase) u ovu funkciju dospeo „radio“, pozvaćemo funkciju radioPicked(radioName), proslediti joj nov parametar – name atribut radio elementa u HTML strukturi dokumenta. Razjasniću malo tu funkciju:
function radioPicked(radioName) {
var radioSet = "";
for (var k=0; k<document.forms.length; k++) {
if (!radioSet) {
radioSet = document.forms[k][radioName];
}
}
if (!radioSet) return false;
for (k=0; k<radioSet.length; k++) {
if (radioSet[k].checked) {
return true;
}
}
return false;
}
Na početku smo deklairsali promenljivu radioSet i dodelili joj prazan string. Ulazimo u for loop i proveravamo sve forme koje imamo na dokumentu, jer kao što sam rekao u prvom delu ovog tutorijala, na strani možemo imati više formi. Mada, ukoliko pogledamo naš html kod, vidimo da u njemu imamo samo jednu formu, pa je ovaj loop za forme nepotreban. Mada, ukoliko se odlučite da stavite više formi na stranicu, ovo će biti svakako neophodan korak.
Zatim, u svakoj formi pronalazimo grupu radio element, uzimamo name atribut i upravo tu vrednost dodeljujemo našoj promenljivoj radioSet. Ukoliko uspemo da promenimo tu promenljivu (koja je definisana kao prazan string), to će značiti da smo našli neki radio element i samim tim možemo da nastavimo. Ali, ukoliko ne nađemo nijedan radio element na strani, radioSet neće dobiti novu vrednost, već će ostati prazan string, pa samim tim znamo da smo u problemu – mi na ovom primeru znamo da imamo radio element. Na kraju, proveravamo radio elemente koje smo „detektovali na dokumentu“ – da li su čekirani i na osnovu toga vraćamo vrednost true ili false.
Kada smo vratili true ili false kao vrednost te funkcije, pogledajmo šta se dešava dalje u funkciji validBasedOnClass() na osnovu ove vraćene vrednosti.
Ukoliko je false, classBack promenljiva postaje „invalid“. Šta to dalje implicira?
- funkcija validBasedOnClass() vraća vrednost invalid
- promenljiva outClass, koju ustvari čine stringovi koje vraća funkcija validBasedOnClass() će sadržati i string invalid.
- zbog toga, invalidTag() funkcija vraća false
- validForm() vraća false
- initForms() vraća false => Naša forma nije validna i neće biti dalje procesirana (u PHP-u npr), već će JavaScript uokviriti HTML element koji pravi problem i na taj način jasno staviti do znanja korisniku šta treba da ispravi.
Sve ovo smo već detaljno prešli u prvom tutorijalu – sada sam samo hteo da još jednom, po tezama prikažem šta se zaista dešava. Mene je logika u ovoj skripti oduševila, a posebno mi se dopada ta uzročno-posledična veza u hijerarhiji definisanih funkcija. Ukoliko najsitniji uslov nije ispunjen u nekom case-u npr, JavaScript zaustavlja dalje submitovanje forme.
Nakon male digresije, dolazimo i do poslednje stvari koje smo želeli da unapredimo – email input polje. Ovde morate dobro zapaziti kako izgleda html fajl. Ja ću namerno postaviti još jednom ovde, da bih vam ukazao na stvari koje morate primetiti kako biste razumeli kod.
label for="emailAddr">Enter your email address: <input id="emailAddr" type="text" size="30" class="reqd email" />
</label></br />
<label for="emailAddr2">Re-enter your email address:<input id="emailAddr2" type="text" size="30" class="reqd emailAddr" />
</label>
Imamo dva input polja: Prvo gde će korisnik ukucati svoju adresu i drugo, u kojoj će korisnik ponovo ukucati tu adresu, koja naravno, mora biti ista kao i ova gornja. Ovo što ću sada napisati je jako, jako važno. Prvo input polje ima dva class atributa: reqd i email. Drugo ima reqd i emailAddr. Obratite sada specijalnu pažnju – emailAddr je id atribut našeg prvog input polja za email. Takođe, jedino emailAddr nismo definisali u našem switch statement-u. Iz tog razloga, kada ta klasa bude proveravana, ona će dospeti pod default u switch-u.
A pogledajmo šta se dešava u default-u:
default:
if (allGood && !crossCheck(thisTag,thisClass)) {
classBack = "invalid ";
}
classBack += thisClass;
E, sada, pošto mi znamo da do defaulta može dospeti baš to input polje koje ima emailAddr klasu, ubacujemo u crossCheck funkciju njegov tag (input) i njegovu klasu (emailAddr). Pogledajmo sada šta se dešava u toj crossCheck funkciji.
function crossCheck(inTag,otherFieldID) {
if (!document.getElementById(otherFieldID)) {
return false;
}
return (inTag.value == document.getElementById(otherFieldID).value);
}
Sve se vidi ovde, ali hajde da prokomentarišem: tražimo element na našem dokumentu koji ima id emailAddr. Naravno, ukoliko ne uspemo da nađemo, vraćamo false. Ali, naravno da ćemo ga naći – to je naše prvo email input polje. Uzimamo njegovu vrednost i poredimo je sa vrednošću drugog email input polja i tu jednakost vraćamo (u vidu true, ili false).
Vratimo se ponovo na default. Ukoliko se vrednosti ova dva polja ne poklapaju, crossCheck vraća false, a naravno, to dalje implicira da će classBack promenljiva biti invalid. Na taj način smo na jedan sjajan način uspeli da proverimo da li je email isti u oba input polja. Sada nam ostaje još i poslednji korak – provera da li je email validan – da budemo sigurni da sadrži @, da @ nije na početku itd….
Pišemo novu funkciju, validEmai(email) i njoj prosleđujemo ono što je korisnik ukucao u to polje – thisTag.value. Meni lično je ovo veoma interesantno rešenje koje je smislila Dori Smith, jer sam prvi put video ovako nešto u programiranju – umesto niza loših karaktera, sastavila je string od loših karaktera i dodelila tu vrednost promenljivoj invalidChar. Dakle, to su neki karakteri koje mi ne želimo da dopustimo korisniku da ukuca u ovo input polje. Evo kako izgleda ta funkcija:
function validEmail(email) {
var invalidChars = " /:,;";
if (email == "") {
return false;
}
for (var k=0; k<invalidChars.length; k++) {
var badChar = invalidChars.charAt(k);
if (email.indexOf(badChar) > -1) {
return false;
}
}
var atPos = email.indexOf("@",1);
if (atPos == -1) {
return false;
}
if (email.indexOf("@",atPos+1) != -1) {
return false;
}
var periodPos = email.indexOf(".",atPos);
if (periodPos == -1) {
return false;
}
return true;
}
1. (prvi IF) proveravamo da li je korisnik nešto ukucao. Ukoliko nije, vratićemo false.
2. (drugi IF) radimo jedan loop kroz email adresu tako što ćemo uvesti brojač k koji kreće od 0 do u ovom slučaju 6 (string invalidChars ima 6 karaktera) i definišemo badChar promenljivu koja će biti prvo razmak, zatim kosa crta ( / ), dve tačke ( : ) i tako dok ne prođe sve karaktere iz InvalidChars promenljive. Za to smo iskoristili već definisano charAt JavaScript funkciju koja radi upravo ovo što sam napisao – uzima karakter pod određenim rednim brojem iz stringa (koji karakter će uzeti, zavisi od brojača k), i zatim indexOf funkcijom proveravamo da li se taj karakter nalazi u email adresi – ukoliko da, indexOf će vratiti tačnu poziciju na kojoj se taj karakter nalazi, što će svakako biti veće od -1, jer u slučaju da se taj karakter prvi u email adresi, indexOf vraća 0 (ukoliko je drugi, indexOf vraća 1, treći – 2 itd). Ukoliko se to desi, vraćamo false.
3. (treći IF) proveravamo gde se nalazi @. Kao što znamo, u email adresi, @ simbol je negde između prvog i poslednjeg karaktera. To znači da ne sme biti ni na prvom, ni na poslednjem mestu. Zato ćemo to i proveriti – doduše, indexOf sada sadrži i drugi parametar, jedinicu. Taj parametar kaže indexOf funkciji odakle da krene da broji @ znak. Jedinica označava da treba da broji od drugog znaka. Mi proveravamo da li je @ na početku stringa. Ukoliko jeste, indexOf će vratiti -1, i zbog toga naravno vraćamo false.
4. (Četvrti IF), dalje, proveravamo da li se, kojim slučajem, u našoj adresi nalaze više od jednog at znaka. Dakle, pitamo da li se negde @ nalazi u stringu od pozicije atPos+1, tj od mesta gde smo pronašli već jedan karakter. Ukoliko postoji, indexOf() neće vratiti -1, pa onda znamo da email nije validan i vraćamo false.
5. (Peti IF) – još jedna bitna stvar kod svakog email-a je njegova struktura. partizan90@gmail.com je primer jedne email adrese. Kao što se vidi, tačka mora da se nalazi negde posle @ znaka. Zato uvodimo novu promenljivu periodPos i njoj dodeljujemo poziciju na kojoj se tačka nalazi počevši od atPos pozicije. Ukoliko je periodPos jednak -1, to znači da nismo našli tačku, pa je email nevalidan i vraćamo false.
Ukoliko je email adresa prošla sve ove naše testove, možemo pretpostaviti da je validna i vraćamo true. Mada i sama Dori Smith je napisala da je veoma teško 100% tačno utvrditi da je email adresa validna, jer bi za to trebalo linije i linije IF-ova…Nama to i nije cilj. Želimo da budemo sigurni da su osnovne stvari zadovoljene, a to smo definitivno i uspeli emailValid() funkcijom.
Ovim smo ujedno i završili JavaScript tutorijal vezan za temu validacije kontakt forme. Videli smo kako jednostavno, brzo i efikasno možemo staviti korisniku do znanja gde je pogrešio u popunjavanju kontakt forme pre nego što je cela forma prosleđena do nekog od server side jezika.Ali, to nikako ne znači da proveru treba prepustiti samo JavaScript-u. To bi bilo vrlo opasno i moglo bi ugroziti bezbednost web aplikacije, pre svega zato što je JavaScript client jezik i korisnik može itekako da se ponaša onako kako mi ne želimo. Zato bih skrenuo pažnju, da pored JavaScript provere obavezno radite i serverside proveru!
U nadi da vam se dopada tuorijal, srdačno vas pozdravljam i pozivam da pratite blog, jer će u budućnosti biti još IT tekstova i saveta.