Walidacja formularzy w JavaScript.


1. Wstęp

Stara prawda mówi: "Waliduj w przeglądarce, ale zawsze sprawdzaj na serwerze". I tego się trzymajmy jak amen pacierza.

JavaScript umożliwia szybką i sprawną walidację danych, bez przeładowywania strony. Wszyscy są zadowoleni, ale... Wyobraźmy sobie, że do akcji przystępuje użytkownik z wyłączoną obsługą JavaScript lub inny, który kopiuje naszą stronę na dysk loklany i usuwa odwołanie do skryptu lub sam skrypt. Świadomie lub nie, jeden i drugi robi śmietnik z naszej bazy, a my lądujemy z przysłowiową ręką w nocniku, pozbawieni jakichkolwiek narzędzi obronnych. A wystarczyło, oprócz JavaScript, zaprząc do pracy choćby PHP. Oj, głupi Polak po szkodzie.

Obsługa wyrażeń regularnych w JavaScript wzorowana jest Perlu i wykorzystuje tradycyjny mechanizm dopasowywania NFA. Wyrażenia regularne reprezentowane są przez obiekty RegExp, które można powołać do życia na dwa sposoby:

  • za pomocą konstruktora
  • za pomocą literału

Listing 1. Tworzenie obiektu RegExp w JavaScript.

new RegExp(wzorzec, flagi); // za pomocą konstruktora
/wzorzec/flagi;  // za pomocą literału

Obiekty RegExp można przypisać do zmiennych:

Listing 2. Przypisywanie obiektów RegExp do zmiennych w JavaScript.

var Wzorzec1 = new RegExp(wzorzec, flagi);  // za pomocą konstruktora z przypisaniem do zmiennej
var Wzorzec2 = /wzorzec/flagi;   // za pomocą literału z przypisaniem do zmiennej

Obiekt RegExp posiada własne właściwości i metody. Ponieważ są one szeroko opisane w sieci, pomijamy ich omawianie i przechodzimy do zastosowań praktycznych.
Na początek korzystając z RegExp, spróbujmy wykonać walidację kodu pocztowego. Wzorzec dopasowania kodu pocztowego tworzymy za pomocą literału:

Listing 3. Wzorzec kodu pocztowego w JavaScript.

var Sz_Kod1 = /^[0-9][0-9]-[0-9][0-9][0-9]$/;
// lub
var Sz_Kod2 = /^[0-9]{2}-[0-9]{3}$/;
// lub
var Sz_Kod3 = /^[\d][\d]-[\d][\d][\d]$/;
// lub
var Sz_Kod4 = /^[\d]{2}-[\d]{3}$/;

Wszystkie wzorce wymuszają wystąpienie cyfry na pierwszym i drugim miejscu, znaku "-" na trzecim miejscu oraz cyfry na czwartym, piątym i szóstym miejscu. Klasa znakowa \d oraz zakres [0-9] są tożsame, oznaczają dowolną cyfrę. Wartość w nawiasie klamrowym oznacza wymaganą liczbę powtórzeń, natomiast kotwice: ^ i znak dolara, odpowiednio: początek i koniec ciągu. Walidacja wprowadzanych danych jest bardzo prosta.

Listing 4. Kod HTML i JavaScript odpowiedzialny za walidację kodu pocztowego.

 <!DOCTYPE html>
<html>
<head>
</head>
<body>
<form name="Kod_Formularz" method="post">
    <input type="text" name="Kod_Pole" />  <input type="submit" id="Kod_Przycisk" value="Sprawdź" />
    <div id="Kod_Komunikat">Wpisz kod (format: 11-222)</div>
</form>
</div>
<script type="text/javascript">
var _kod_wzorzec =  /^[0-9]{2}-[0-9]{3}$/;
var _kod_komunikat = document.getElementById("Kod_Komunikat");
var _kod_sprawdz = document.getElementById("Kod_Przycisk");
_kod_sprawdz.onclick = F_Kod;
function F_Kod() {
    var KOD = document.Kod_Formularz.Kod_Pole.value;
    // var KOD = document.forms["Kod_Formularz"]["Kod_Pole"].value; // zamiennie
    if (KOD == null || KOD == "") { _kod_komunikat.innerHTML = "Brak kodu"; }
    else if (_kod_wzorzec.test(KOD)) { _kod_komunikat.innerHTML = "Kod poprawny"; }
    else { _kod_komunikat.innerHTML = "Kod niepoprawny"; };
    return false;
};
</script>
</body>
</html>

Przykład. Walidacja kodu pocztowego na podstawie Listingu 4.

  
Wpisz kod (format: 11-222)

Z Listingu 4 omówienia wymaga jedynie warunek if, a dokładnie metoda obiektu RegExp o nazwie test. Meotoda ta sprawdza, czy w zmiennej KOD, czyli aktualnej wartości pola input zawiera się wzorzec RegExp przypisany do zmiennej _kod_wzorzec i jeżeli tak zwraca true. Warunek działa zatem na zasadzie, jeżeli prawda...zrób to, w przeciwnym przypadku...zrób tamto.

W podobny sposób możemy walidować: imię i nazwisko, adres, miejscowość, telefon - wystarczy dopasować wzorzec. Czasami zachodzi jednak potrzeba bardziej wyrafinowanej walidacji niż na poziomie wyrażeń regularnych. Na przykład, chcąc dokładnie walidować NIP, należy sprawdzić jego sumę kontrolną.

Listing 5. Kod HTML i JavaScript odpowiedzialny za walidację NIP.

 <!DOCTYPE html>
<html>
<head>
</head>
<body>
<form name="NIP_Formularz" method="post">
    <input type="text" name="NIP_Pole" />  <input type="submit" id="NIP_Przycisk" value="Sprawdź" />
    <div id="NIP_Komunikat">Wpisz NIP (format: 1234567891)</div>
</form>
<script type="text/javascript">
var _nip_komunikat = document.getElementById("NIP_Komunikat");
var _nip_sprawdz = document.getElementById("NIP_Przycisk");
_nip_sprawdz.onclick = F_NIP;
function F_NIP() {
    var NIP = document.NIP_Formularz.NIP_Pole.value;
    // var NIP = document.forms["NIP_Formularz"]["NIP_Pole"].value; // zamiennie
    if (NIP == null || NIP == "") { _nip_komunikat.innerHTML = "Brak NIP"; }
    else if (NIP == "1234567890") { _nip_komunikat.innerHTML = "NIP niepoprawny"; } // obsluga wyjatku
    else {
	    NIP = NIP.replace(/[\s-]/g, "");
	    var CyfraKontrolna = parseInt(NIP.charAt(9));
	    var TablicaWag = [6, 5, 7, 2, 3, 4, 5, 6, 7];
	    if (NIP.length == 10 && parseInt(NIP, 10) > 0) {
		    var SumaKontrolna = 0;
		    for(var i = 0; i<9; i++) {
			    SumaKontrolna += parseInt(NIP.charAt(i)) * TablicaWag[i];
		    }                     
		    var WynikKontrolnyModulo = SumaKontrolna % 11;
		    var ResztaKontrolna = (WynikKontrolnyModulo == 10)?0:WynikKontrolnyModulo;
		    if (ResztaKontrolna == CyfraKontrolna) { _nip_komunikat.innerHTML = "NIP poprawny"; }
		    else { _nip_komunikat.innerHTML = "NIP niepoprawny"; };
	    }
	    else { _nip_komunikat.innerHTML = "NIP niepoprawny"; };
    };
return false;
};
</script>
</body>
</html>

Przykład. Walidacja NIP na podstawie Listingu 5.

  
Wpisz NIP (format: 1234567891)

Przyjrzyjmy się jak działa metoda F_NIP(). Najpierw do zmiennej NIP przypisywana jest wartość pola o nazwie NIP_Pole z formularza o nazwie NIP_Formularz. Krótko mówiąc, trafia do niej to, co wprowadzamy w polu tekstowym, czyli łańcuch znaków. Sprawdzamy, czy nie jest ona pusta lub NULL oraz wyjątek i jeżeli tak, wyświetlamy komunikat o braku lub niepoprawności NIP, a jeżeli nie przystępujemy do walidacji NIP (zob. Numeracja NIP). Korzystając z metody replace() usuwamy wszystkie spacje i myślniki (znaki "-") ze zmiennej NIP, po czym za pomocą metody charAt() pobieramy z niej znak z pozycji 9* (licząc od zera, czyli znak 10 licząc od 1) i przypisujemy go do zmiennej CyfraKontrolna. Tworzymy tablicę jednowymiarową, której elementami są obowiązujące wagi urzędowe. Sprawdzamy, czy długość zmiennej NIP wynosi 10 i czy jest ona większa od 0* i jeżeli tak deklarujemy zmienną SumaKontrolna nadając jej wartość 0. Za pomocą pętli for iterujemy po wszystkich znakach zmiennej NIP, dodając do zmiennej SumaKontrolna kolejne iloczyny i-tego znaku i odpowiadającej mu wagi z tablicy TablicaWag. Następnie wykonujemy dzielenie modulo przez 11 zmiennej SumaKontrolna, przypisując resztę z dzielenia do zmiennej WynikKontrolnyModulo. Sprawdzamy, czy zmienna WynikKontrolnyModulo nie jest równa 10 (z urzędowego założenia niemożliwe) i jeżeli tak - to do zmiennej ResztaKontrolna przypisujemy 0, jeżeli nie przypisujemy do niej zmienną WynikKontrolnyModulo. Sprawdzamy, czy zmienna ResztaKontrolna jest równa zmiennej CyfraKontrolna. W przypadku powodzenia wyświetlamy komunikat o poprawności NIP, w przypadku fiaska o jego niepoprawności. Przeanalizujmy cały kod na przykładzie. Załóżmy, że użytkownik wprowadza NIP: 894-24-93-544, co oznacza:

przypisz do zmiennej NIP wprowadzony łańcuch znaków (NIP = "894-24-93-544")
jeżeli zmienna NIP jest równa null lub zmienna NIP jest pusta, wyświetl komunikat "Brak NIP" (co dla NIP = "894-24-93-544" nie zachodzi)
a jeżeli zmienna NIP jest równa "1234567890" (wyjątek), wyświetl komunikat "NIP niepoprawny" (co dla NIP = "894-24-93-544" nie zachodzi)
w przeciwnym razie (co dla NIP = "894-24-93-544" zachodzi)
usuń ze zmiennej NIP wszystkie myślniki (NIP = "8942493544")
przypisz do zmiennej CyfraKontrolna dziewiąty znak zmiennej NIP* czyli ostatnią czwórkę (CyfraKontrolna = 4)
utwórz tablicę wag (TablicaWag = [6, 5, 7, 2, 3, 4, 5, 6, 7])
jeżeli liczba znaków zmiennej NIP jest równa 10 i zmienna NIP jako liczba całkowita* jest większa od 0 (co dla 8942493544 zachodzi)
oblicz sumę kontrolną (SumaKontrolna = 8*6+9*5+4*7+2*2+4*3+9*4+3*5+5*6+4*7, SumaKontrolna = 246)*
podziel modulo zmienną SumaKontrolna przez 11 i przypisz do zmiennej WynikKontrolnyModulo (WynikKontrolnyModulo = 246 % 11, WynikKontrolnyModulo = 4)
jeżeli zmienna WynikKontrolnyModulo jest równa 10, przypisz do zmiennej ResztaKontrolna 0 (co dla 4 nie zachodzi)
w przeciwnym razie przypisz zmiennej ResztaKontrolna zmienną WynikKontrolnyModulo (co dla 4 zachodzi, ResztaKontrolna = 4)
jeżeli zmienna ResztaKontrolna jest równa zmiennej WynikKontrolnyModulo (co dla 4 = 4 zachodzi), wyświetl komunikat "NIP poprawny"
w przeciwnym razie wyświetl komunikat "NIP niepoprawny" (co dla 4 = 4 nie zachodzi)
w przeciwnym razie wyświetl komunikat "NIP niepoprawny" (co dla 8942493544 nie zachodzi)

*Metoda parseInt() zwraca nam liczbę całkowitą (tu w systemie dziesiętnym - drugi argument równy 10)

2. Czy o to nam chodziło...

Walidacja pojedynczych pól formularza jest zatem stosunkowo łatwa - wystarczy pobrać wartość pola i porównać ją z wzorcem dopasowania, korzystając z metody test. Zważywszy, że w sieci dostępnych jest setki wzorców, bez problemu znaleźć można potrzebne wyrażenia. Czy jednak o to nam chodzi, by w nieskończoność powielać rozwiązania na zasadzie kopiuj-wklej i wszystko robić na piechotę. Czy nie lepiej napisać własną klasę, którą w razie potrzeby można będzie wielokrotnie wykorzystywać, bez pisania w kółko tego samego kodu. Z pewnością, tak.

O klasach (których nie ma...) pisaliśmy szerzej w artykule 1.09 Zabawa w klasy. Pominiemy więc część teroretyczną i od razy przystąpimy do rozważań na temat tego, co i jak ma robić nasza "klasa". Ponieważ na jednej stronie zamierzamy przetwarzać dowolną liczbę formularzy, każdy formularz i obsługujący go przycisk muszą mieć unikalne atrybuty, którymi w tym przypadku będą wartości atrybutów: name oraz id. Wartości te przekażemy konstruktorowi jako argumenty, który na ich podstawie obsłuży zdarzenie onlcick() - kliknięcie przycisku. Dodatkowo stworzymy metodę pobierającą do tablic informacje o polach formularza, a mianowicie ich nazwach, typach i wzorcach dopasowania. Informacje te przekażemy również w postaci argumentów. Innymi słowy, tuż po utworzeniu, każdy nowy obiekt powinien posiadać:

  • odwołanie do obsługującego go przycisku
  • obsługę kliknięcia tego przycisku
  • własną metodę zbierającą informacje o wszystkich obsługiwanych polach formularza (nazwie pola, typie pola, wzorcu dopasowania)

Napiszmy szkielet funkcji pełniącej rolę konstruktora, formularze oraz przynajmniej jeden wzorzec i sprawdźmy działanie naszego kodu, wyświetlając identyfikator przycisku i nazwę formularza.

Listing 6. Szkielet funkcji JavaScript pełniącej rolę konstruktora.

function K_Walidacja(F_Nazwa,PrzyciskID) {
    var Tablica_N = new Array(), Tablica_T = new Array(), Tablica_S = new Array(), Tablica_Licznik = 0;
    this.Przycisk = document.getElementById(PrzyciskID);
    // Metoda dopisująca do tablic kolejne elementy na podstawie otrzymanych argumentów
    this.M_Pole = function(P_Nazwa,P_Typ,P_Wzorzec) {
        Tablica_N[Tablica_Licznik] = P_Nazwa;      // Tablica z nazwami pól
        Tablica_T[Tablica_Licznik] = P_Typ;          // Tablica z typami pól
        Tablica_S[Tablica_Licznik] = P_Wzorzec;   // Tablica z wzorcami dopasowania pól
        Tablica_Licznik++;
    };
    // Obsluga przycisku (reakcja na zdarzenie onclick)
    this.Przycisk.onclick = function() { 
        // przykładowa akcja
        alert(PrzyciskID+" "+F_Nazwa);
        return false;
    };
};

Kod jest prosty - najpierw tworzymy trzy puste tablice oraz deklarujemy zmienną Tablica_Licznik o wartości 0. Potem, za pomocą identyfikatora (argument konstruktora), pobieramy odwołanie do przycisku, a za pomocą metody M_Pole dopisujemy do tablic informacje o polach formularza (argumenty metody), po każdym polu zwiększając indeks o 1. Na końcu obsługujemy kliknięcie przycisku, wyświetlając tymczasowy komunikat z identyfikatorem przycisku i nazwą formularza. Dzięki użyciu wskaźnika this odwołujemy się do bieżącego obiektu.

Listing 7. Kod HTML odpowiedzialny za utworzenie formularzy. Kod JavaScript odpowiedzialny za utworzenie obiektów.

<form name="F_Waliduj01" method="post">
	<input type="text" name="P_Pole01" /> <input type="submit" id="F_Przycisk01" value="Sprawdź" />
</form>
<form name="F_Waliduj02" method="post">
	<input type="text" name="P_Pole02" /> <input type="submit" id="F_Przycisk02" value="Sprawdź" />
</form>
<form name="F_Waliduj03" method="post">
	<input type="text" name="P_Pole03" /> <input type="submit" id="F_Przycisk03" value="Sprawdź" />
</form>

<script type="text/javascript">
var WzorzecNiepuste = /\S{3}/;
// Obiekt 1
var O_Walidacja01 = new K_Walidacja("F_Waliduj01","F_Przycisk01");
O_Walidacja01.M_Pole("P_Pole01","Pole",WzorzecNiepuste);
// Obiekt 2
var O_Walidacja02 = new K_Walidacja("F_Waliduj02","F_Przycisk02");
O_Walidacja02.M_Pole("P_Pole02","Pole",WzorzecNiepuste);
// Obiekt 3
var O_Walidacja03 = new K_Walidacja("F_Waliduj03","F_Przycisk03");
O_Walidacja03.M_Pole("P_Pole03","Pole",WzorzecNiepuste);
</script>

Przykład. Wyświetlenie identyfikatora przycisku i nazwy formularza podstawie Listingów 6, 7.

Po napisaniu szkieletu funkcji konstruktora możemy zdefiniować właściwą akcję podejmowaną po kliknięciu przycisku. W tym celu wprowadzimy dodatkową zmienną P_Status o wartości początkowej 0, która będzie postinkrementowana (+1) lub postdekrementowana (-1), w zależności od tego, czy walidacja wartości pola będzie poprawna czy błędna. Zmienna ta pozwoli kontrolować nam zachowanie formularza, ponieważ w przypadku prawidłowej walidacji wszystkich pól, musi być ona równa liczbie elementów jednej z tablic: Tablica_N, Tablica_T lub Tablica_S (obojętnie której, gdyż wszystkie mają taką samą liczbę elementów). Ponadto utworzymy prototyp i metodę prototypu odpowiedzialną za walidację wartości pól. Metodzie tej będziemy przekazywać elementy tablic, po każdym kliknięciu przycisku formularza.

Spróbujmy najpierw wyświetlić aktualną wartość pola.

Listing 8. Kod JavaScript wyświetlający aktualną wartość pola.

<script type="text/javascript">
function K_Walidacja(F_Nazwa,PrzyciskID) {
    var Tablica_N = new Array(), Tablica_T = new Array(), Tablica_S = new Array(), Tablica_Licznik = 0;
    this.Przycisk = document.getElementById(PrzyciskID);
    // Metoda dopisująca do tablic kolejne elementy na podstawie otrzymanych argumentów
    this.M_Pole = function(P_Nazwa,P_Typ,P_Wzorzec) {
        Tablica_N[Tablica_Licznik] = P_Nazwa;      // Tablica z nazwami pól
        Tablica_T[Tablica_Licznik] = P_Typ;          // Tablica z typami pól
        Tablica_S[Tablica_Licznik] = P_Wzorzec;   // Tablica z wzorcami dopasowania pól
        Tablica_Licznik++;
    };
    // Obsluga przycisku (reakcja na zdarzenie onclick)
    this.Przycisk.onclick = function() { 
        // akcja
        var Tablica_Dlugosc = Tablica_N.length;
        for (var i=0; i<Tablica_Dlugosc; i++) {	
            M_SprawdzPole(F_Nazwa,Tablica_N[i],Tablica_T[i],Tablica_S[i]);
        };
        return false;
    };
};
K_Walidacja.prototype.Prototyp = function() {
    M_SprawdzPole = function(F_Nazwa,P_Nazwa,P_Typ,P_Wzorzec) {    
        var P_Wartosc = document.forms[F_Nazwa][P_Nazwa].value;
        alert(P_Wartosc); 
    };
};
</script>

Utworzenie obiektu w JavaScript będzie wymagać dodania prototypu.

Listing 9. Kod HTML odpowiedzialny za utworzenie formularzy. Kod JavaScript odpowiedzialny za utworzenie obiektów.

<script type="text/javascript">
var WzorzecNiepuste = /\S{3}/;
// Obiekt nnn
var O_Walidacja_nnn = new K_Walidacja("F_Waliduj_nnn","F_Przycisk_nnn");
O_Walidacja_nnn.Prototyp();
O_Walidacja_nnn.M_Pole("P_Pole_nnn","Pole",WzorzecNiepuste);
</script>

Przykład. Wyświetlanie aktualnej wartości pola na podstawie Listingów 8, 9.

Teraz, aby sprawdzić wartość pola wystarczy zamiast instrukcji alert(P_Wartosc), użyć P_Wzorzec.test(P_Wartosc) (wzorzec przekazujemy metodzie M_SprawdzPole() jako argument) i wyświetlić opdowiedni komunikat, np.
    if (P_Wzorzec.test(P_Wartosc)) { alert("OK"); } else { alert("Błąd"); }
co oznacza, jeżeli metoda test zwróci prawdę wyświetl OK, w przeciwnym przypadku wyświetl Błąd.

Co jednak, gdy zamiast jednego pola, będziemy chcieli walidować wiele pól formularza. Jak rozpoznać, czy wszystkie zostały wypełnione prawidłowo? Otóż, w tym celu wykorzystamy wspominaną już zmienną P_Status, która zliczać będzie poprawnie wypełnione pola. Przy okazji napiszemy też metodę odpowiedzialną za wyświetlanie komunikatów oraz metodę walidującą wybrane numery: NIP, REGON (9-cyfrowy), PESEL.

Listing 10. Kod JavaScript walidujący wartości pól formularzy na podstawie pobranych wzorców dopasowania. Plik: walidacja.js

<script type="text/javascript">
// Script: Walidacja formularzy
// Author: Ireneusz Sekula, http://secom.pl, 2011-09-21
// Free to use and abuse under the MIT license.

function K_Walidacja(F_Nazwa,PrzyciskID) {
    var Tablica_Licznik = 0; P_Status = 0;
    var Tablica_N = new Array(), Tablica_T = new Array(), Tablica_S = new Array();
    this.Przycisk = document.getElementById(PrzyciskID);

    this.M_Pole = function(P_Nazwa,P_Typ,P_Wzorzec) {
        Tablica_N[Tablica_Licznik] = P_Nazwa;
        Tablica_T[Tablica_Licznik] = P_Typ;
        Tablica_S[Tablica_Licznik] = P_Wzorzec;
        Tablica_Licznik++;
    };
    this.Przycisk.onclick = function() { 
        var Tablica_Dlugosc = Tablica_N.length;
        for (var i=0; i<Tablica_Dlugosc; i++) {	
            M_SprawdzPole(F_Nazwa,Tablica_N[i],Tablica_T[i],Tablica_S[i]);
        };
        if (P_Status == Tablica_Dlugosc) {
            M_Komunikat(F_Nazwa,"Formularz wypełniony prawidłowo!");
            P_Status = 0;
            return false; // zakomentuj jezeli chcesz submitowac formularz lub wstaw akcje lub return true; 
        }
        else { 
            M_Komunikat(F_Nazwa,"Formularz wypełniony nieprawidłowo!");
            P_Status = 0;
            return false;
        };
    };
};
K_Walidacja.prototype.Prototyp = function() {
    M_SprawdzPole = function(F_Nazwa,P_Nazwa,P_Typ,P_Wzorzec) {    
        var P_Wartosc = document.forms[F_Nazwa][P_Nazwa].value;
        var P_Element = document.forms[F_Nazwa][P_Nazwa];
        // mozna usuwac spacje i myslniki z pobranych wartosci
        // if (P_Typ == "Tel" || P_Typ == "NIP") { P_Wartosc = P_Wartosc.replace(/[\s-]/g, ""); };

        if (P_Typ == "NIP" || P_Typ == "REGON9" || P_Typ == "PESEL") {
            if (M_SprawdzNumer(P_Wartosc,P_Typ)) {
                P_Element.className = "K_Walid_OK";
                P_Status++;
            }
            else {
                P_Element.className = "K_Walid_Bl";
                P_Status--;
            };
        }
        else if (P_Wzorzec.test(P_Wartosc)) { 
            P_Element.className = "K_Walid_OK";
            P_Status++;
        }
        else { 
            P_Element.className = "K_Walid_Bl";
            P_Status--;
        };
    };
    M_SprawdzNumer = function(P_Wartosc,P_Typ) {
        if (P_Typ == "NIP") {
            if (P_Wartosc == "1234567890") { return false; };
            var P_Dlugosc = 10; 
            var CyfraKontrolna = parseInt(P_Wartosc.charAt(9));
            var TablicaWag = [6, 5, 7, 2, 3, 4, 5, 6, 7];
            var Dzielnik = 11;
        };
        if (P_Typ == "REGON9") {
            var P_Dlugosc = 9; 
            var CyfraKontrolna = parseInt(P_Wartosc.charAt(8));
            var TablicaWag = [8, 9, 2, 3, 4, 5, 6, 7];
            var Dzielnik = 11;
        };
        if (P_Typ == "PESEL") {
            var P_Dlugosc = 11; 
            var CyfraKontrolna = parseInt(P_Wartosc.charAt(10));
            var TablicaWag = [9, 7, 3, 1, 9, 7, 3, 1, 9, 7];
            var Dzielnik = 10;
        };
        if (P_Wartosc.length != P_Dlugosc || parseInt(P_Wartosc, 10) <= 0) { return false; };
        var SumaKontrolna = 0;
        for (var i = 0; i<P_Dlugosc-1; i++) {
            SumaKontrolna += parseInt(P_Wartosc.charAt(i)) * TablicaWag[i];
        }                     
        var WynikKontrolnyModulo = SumaKontrolna % Dzielnik;
        var ResztaKontrolna = (WynikKontrolnyModulo == 10)?0:WynikKontrolnyModulo;
        if (ResztaKontrolna == CyfraKontrolna) { return true; }
        else { return false; };
    };
    M_Komunikat = function(F_Nazwa,K_Tekst) {
        var S_Komunikat = document.getElementById("K_"+F_Nazwa);
        if (S_Komunikat != null) { S_Komunikat.parentNode.removeChild(S_Komunikat); }
        var Komunikat=document.createElement("div");
        Komunikat.setAttribute("id", "K_"+F_Nazwa);
        // Komunikat.setAttribute("class", "K_Walid_Komunikat"); // nie dziala pod IE
        Komunikat.className = "K_Walid_Komunikat";
        var KomunikatTekst = document.createTextNode(K_Tekst);
        Komunikat.appendChild(KomunikatTekst);
        document.forms[F_Nazwa].appendChild(Komunikat);
    };
};
</script>

Przeanalizujmy, co dzieje się po kliknięciu przycisku czyli jak działa metoda Przycisk.onclick. Najpierw obliczana jest długość jednej z tablic (wszystkie mają taką samą liczbę elementów). Potem w pętli for wywoływana jest metoda M_SprawdzPole(), której argumentami są nazwa formularza oraz kolejene elementy tablic według ich indeksów (od 0 do ostatniego). Metoda ta każdorazowo pobiera z tablic wszystkie informacje o pojedynczym polu, przekazywane przez nas metodzie M_Pole(). Przykładowo, dla O_Walidacja1.M_Pole("P_Firma1","Firma",WzorzecFirma) będą to: "P_Firma1", "Firma", WzorzecFirma, a dla O_Walidacja1.M_Pole("P_Kod1","Kod",WzorzecKod): "P_Kod1", "Kod", WzorzecKod. Jak łatwo zauważyć, wartości te odpowiadają: nazwie, typie i wzorcowi dopasowania pola, a kolejność sprawdzania pól odpowiada kolejności wywoływania przez nas metody M_Pole().

Na podstawie argumentów F_Nazwa i P_Nazwa (nazwa formularza i nazwa pola), metoda M_SprawdzPole() pobiera wartość pola danego formularza (zmienna P_Wartosc) i odwołanie do tego pola (zmienna P_Element), po czym sprawdza typ pola (argument P_Typ)).

Jeżeli jest to pole specjalne, np. NIP, REGON, PESEL wywołuje ona metodę M_SprawdzNumer() walidującą wartość pola według przyjętego algorytmu, przekazując jej jako argumenty wartość i typ pola. Ponieważ weryfikacja numerów NIP, REGON, PESEL różni się miejscem cyfry kontrolnej, tablicą wag, dzielnikiem przy dzieleniu modulo oraz długością samego numeru - metoda M_SprawdzNumer() ponownie bada typ pola i przypisuje zmiennym wartości zgodne z urzędowymi. Dalszy etap walidacji jest identyczny z opisaną przez nas wcześniej procedurą sprawdzania numeru NIP. W przypadku powodzenia metoda M_SprawdzNumer() zwraca true, w przypadku niepowodzenia false - dzięki czemu metoda M_SprawdzPole() kontrolująca zwracane wartości, postinkrementuje zmienną P_Status i nadaje sprawdzanemu polu nazwę klasy K_Walid_OK lub postdekrementuje zmienną P_Status i nadaje polu nazwę klasy K_Walid_Bl. Klasa K_Walid_OK wyświetla ramkę pola na zielono, a K_Walid_Bl na czerwono.

Jeżeli jest to pole zwykłe, metoda M_SprawdzPole() pomija wywołanie metody M_SprawdzNumer() i za pomocą metody test() rozpoczyna walidację wartości pola według otrzymanego wzorca dopasowania (argument P_Wzorzec). W przypadku powodzenia metoda test() zwraca true, w przypadku niepowodzenia false - dzięki czemu metoda M_SprawdzPole() w zależności od wyniku, może podobnie jak wyżej, postinkrementować lub postdekrementować zmienną P_Status i nadawać sprawdzanemu polu odpowiednią nazwę klasy.

Po zakończeniu wywoływania metody M_SprawdzPole() w pętli for, metoda Przycisk.onclick, sprawdza, czy zmienna P_Status równa jest długości tablicy (czyli, czy wszystkie walidacje przebiegły poprawnie) i jeżeli tak wyzerowuje zmienną P_Status, wyświetla komunikat o powodzeniu i podejmuje akcję końcową, np. zwraca true przekazując formularz we władanie PHP. W przeciwnym przypadku, metoda Przycisk.onclick wyzerowuje zmienną P_Status, wyświetla komunikat o niepowodzeniu i zwraca false.

Przykładowy kod HTML oraz JavaScript obsługujący skrypt przedstawiony jest poniżej. Stylowanie formularza zależy od upodobań użytkownika. Wzorce należy umieścić w osobnym pliku.

Listing 11. Kod HTML odpowiedzialny za utworzenie formularzy. Kod JavaScript odpowiedzialny za utworzenie obiektów (obsługujący skrypt z Listingu 10).

 <!DOCTYPE html>
<html>
<head>
<style type="text/css">
div.K_Walidacja table { width:280px; }
div.K_Walidacja table td { padding:0px; }
div.K_Walidacja table td input, div.K_Walidacja table td textarea { width:150px; padding:2px; }
.K_Walid_Komunikat { /*display:none;*/ /*odznacz, jezeli nie chesz wyswietlac komunikatow*/}
.K_Walid_OK   { border-color:green; }
.K_Walid_Bl { border-color:red;  }
</style>
</head>
<body>
<!-- pierwszy formularz -->
<div class="K_Walidacja">
    <form name="F_Waliduj1" method="post">
        <table cellpadding="0" cellspacing="0"><tbody>
            <tr><td>Nazwa firmy:</td><td><input type="text" name="P_Firma1" /></td></tr>
            <tr><td>Ulica:</td><td><input type="text" name="P_Ulica1" /></td></tr>
            <tr><td>Kod pocztowy:</td><td><input type="text" name="P_Kod1" /></td></tr>
            <tr><td>Miejscowość:</td><td><input type="text" name="P_Miasto1" /></td></tr>
            <tr><td>NIP:</td><td><input type="text" name="P_NIP1" /> </td></tr>
            <tr><td>REGON9:</td><td><input type="text" name="P_REGON1" /> </td></tr>
            <tr><td>PESEL:</td><td><input type="text" name="P_PESEL1" /> </td></tr>
            <tr><td>Imię i nazwisko:</td><td><input type="text" name="P_Kontakt1" /> </td></tr>
            <tr><td>Telefon:</td><td><input type="text" name="P_Tel1" /> </td></tr>
            <tr><td>E-mail:</td><td><input type="text" name="P_Email1" /> </td></tr>
            <tr><td>Uwagi:</td><td><textarea name="P_Tekst1"></textarea></td></tr>
            </tbody></table>
            <p><input type="submit" id="F_Przycisk1" value="Sprawdź" /></p>
    </form>
</div>
<!-- drugi formularz -->
<div class="K_Walidacja">
    <form name="F_Waliduj2" method="post">
        <table cellpadding="0" cellspacing="0"><tbody>
            <tr><td>Imię i nazwisko:</td><td><input type="text" name="P_Kontakt2" /> </td></tr>
            <tr><td>Telefon:</td><td><input type="text" name="P_Tel2" /> </td></tr>
            <tr><td>E-mail:</td><td><input type="text" name="P_Email2" /> </td></tr>
            <tr><td>Uwagi:</td><td><textarea name="P_Tekst2"></textarea></td></tr>
        </tbody></table>
        <p><input type="submit" id="F_Przycisk2" value="Sprawdź" /></p>
    </form>
</div>
<script src="walidacja.js" type="text/javascript"></script>
<script type="text/javascript">
// wzorce - umieść je w osobnym pliku
var WzorzecFirma = /^[A-Za-zĄĘĆŁŃÓŚŻŹąęćłńóśżź0-9@!%&+-_*.,:\s\'\"\/]+$/;
var WzorzecUlica = /^[A-Za-zĄĘĆŁŃÓŚŻŹąęćłńóśżź]{3,100}$|^[A-Za-zĄĘĆŁŃÓŚŻŹąęćłńóśżź]+[A-Za-zĄĘĆŁŃÓŚŻŹąęćłńóśżź0-9\s\-\.\/]+[A-Za-zĄĘĆŁŃÓŚŻŹąęćłńóśżź0-9]{1,100}$/;
var WzorzecKod = /^[0-9]{2}-[0-9]{3}$/;
var WzorzecMiasto = /^[A-Za-zĄĘĆŁŃÓŚŻŹąęćłńóśżź]{3,100}$|^[A-Za-zĄĘĆŁŃÓŚŻŹąęćłńóśżź]+[A-Za-zĄĘĆŁŃÓŚŻŹąęćłńóśżź0-9\s\-]+[A-Za-zĄĘĆŁŃÓŚŻŹąęćłńóśżź0-9]{1,100}$/;
var WzorzecKontakt = /^[A-ZŚŹŻŁĆa-zęłćńóżąźś]+\s[A-ZŚŹŻŁĆa-zęłćńóżąźś]+$|^[A-ZŚŹŻŁĆa-zęłćńóżąźś]+\s[A-ZŚŹŻŁĆa-zęłćńóżąźś]+[\-\s]+[A-ZŚŹŻŁĆa-zęłćńóżąźś]+$/;
var WzorzecTel = /^[0-9]{9}$/;
var WzorzecNiepuste = /\S{3}/;
var WzorzecEmail = /^\s*[\w\-\+_]+(\.[\w\-\+_]+)*\@[\w\-\+_]+\.[\w\-\+_]+(\.[\w\-\+_]+)*\s*$/;
</script>
<script type="text/javascript">
// pierwszy formularz
var O_Walidacja1 = new K_Walidacja("F_Waliduj1","F_Przycisk1");
O_Walidacja1.Prototyp();
O_Walidacja1.M_Pole("P_Firma1","Firma",WzorzecFirma);
O_Walidacja1.M_Pole("P_Ulica1","Ulica",WzorzecUlica);
O_Walidacja1.M_Pole("P_Kod1","Kod",WzorzecKod);
O_Walidacja1.M_Pole("P_Miasto1","Miasto",WzorzecMiasto);
O_Walidacja1.M_Pole("P_NIP1","NIP","");
O_Walidacja1.M_Pole("P_REGON1","REGON9","");
O_Walidacja1.M_Pole("P_PESEL1","PESEL","");
O_Walidacja1.M_Pole("P_Kontakt1","Kontakt",WzorzecKontakt);
O_Walidacja1.M_Pole("P_Tel1","Tel",WzorzecTel);
O_Walidacja1.M_Pole("P_Email1","Email",WzorzecEmail);
O_Walidacja1.M_Pole("P_Tekst1","Tekst",WzorzecNiepuste);
// drugi formularz
var O_Walidacja2 = new K_Walidacja("F_Waliduj2","F_Przycisk2");
O_Walidacja2.Prototyp();
O_Walidacja2.M_Pole("P_Kontakt2","Kontakt",WzorzecKontakt);
O_Walidacja2.M_Pole("P_Tel2","Tel",WzorzecTel);
O_Walidacja2.M_Pole("P_Email2","Email",WzorzecEmail);
O_Walidacja2.M_Pole("P_Tekst2","Tekst",WzorzecNiepuste);
</script>
</body>
</html>

Przykład. Walidacja dwóch formularzy na podstawie Listingów 10, 11.

Nazwa firmy:
Ulica:
Kod pocztowy:
Miejscowość:
NIP:
REGON9:
PESEL:
Imię i nazwisko:
Telefon:
E-mail:
Uwagi:

Imię i nazwisko:
Telefon:
E-mail:
Uwagi:

Zobacz demo: Walidacja formularzy

Ireneusz Sekuła, JavaScript dla zielonych