Za pomocą Javascriptu możemy w łatwy sposób kontrolować zachowanie się formularzy znajdujących się na stronie.
Częstokroć do kontroli danych pochodzących z formularzy stosuje się skrypty działające po stronie serwera (np w technologii PHP, CGI).
Czasami chcemy mieć możliwość natychmiastowej reakcji na poczynania użytkownika (np. wyświetlenie informacji gdy użytkownik wprowadzi błędne imię). Wysłane dane można oczywiście dalej sprawdzić za pomocą wyżej wymienionych języków, co da nam o wiele większe szanse na to, że dane te są prawidłowo wprowadzone.
Istnieje kilka sposobów odwoływania się do elementów formularzy.
Stwórzmy przykładowy formularz na którym będziemy pracować:
<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>
Aby teraz odwołać się do atrybutu value pola "Imie" skorzystamy z konstrukcji:
document.forms[0].elements[1].value //pierwszym elementem jest pierwszy fieldset document.Formularz.Imie.value document.forms['Formularz'].elements['Imie'].value document.Formularz.elements['Imie'].value document.forms['Formularz'].Imie.value
Poza pierwszym z powyższych odwołań, wszystkie odwołania korzystają z atrybutu name Formularza.
W HTML Strict nie możemy jednak stosować atrybutu name dla Formularzy. Nie jest to żaden problem. Skorzystamy z metody getElementById.
document.getElementById('Formularz');
document.getElementById('Imie').value;
Czasami najwygodniej odwoływać się do formularzy z wykorzystaniem obydwu powyższych sposobów.
Wyobraź sobie, że na stronie masz dwa formularze. W każdym z nich znajduje się imię do którego użytkownik powinien wpisać... imię.
Jak nazwać takie pola? Pierwsze z nich mogło by mieć identyfikator id="Imie1", drugie pole id="Imie2".
Jeżeli takich form na stronie było by nieco więcej - powiedzmy 100 (generowane dynamicznie),
musiałbyś być bardzo uważny przy manipulacji takimi polami. Imie1, Imie2, Imie3 itp.
Łatwo się w tym wszystkim pogubić. O wiele lepszym rozwiązaniem jest odwoływanie się do samej formy,
a następnie pobieranie jej elementów po ich nazwach.
Dzięki temu nie musisz nawet stosować atrybutu ID dla poszczególnych pól, a ich nazwy w kolejnych formularzach mogą być takie same
(dzięki temu o wiele łatwiej jest manipulować kilkoma takimi samymi formami na stronie).
Nie jest to najbardziej semantyczny sposób na świecie, ale na pewno jest wygodny. Spójrz na poniższy kod:
//Funkcja zwraca tablicę z wartościami pól z danego formularza
function pobierz_dane(id_formy) {
var formularz = document.getElementById(id_formy);
var imie = formularz.Imie.value
var nazwisko = formularz.Nazwisko.value;
var notatki = formularz.Notatki.value
return [Imie, Nazwisko, Notatki];
}
document.write('[' + pobierz_dane('Formularz_1').join('] [') + ']');
...
<form id="Formularz_1">
<fieldset>
<inpu type="text" name="Imie" />
<inpu type="text" name="Nazwisko" />
<inpu type="text" name="Notatki" />
</fieldset>
</form>
<form id="Formularz_2">
<fieldset>
<inpu type="text" name="Imie" />
<inpu type="text" name="Nazwisko" />
<inpu type="text" name="Notatki" />
</fieldset>
</form>
Aby odczytać wartość pola typu text musimy odwołać się do jego właściwości value:
document.getElementById('Imie').value
document.getElementById('Formularz').Imie.value
Wartość ta jest ciągiem znaków, możemy więc wykonać na niej wszystkie operacje, które możemy wykonywać na ciągach. I tak na przykład:
var wartosc = document.getElementById("Tekst").value;
if (wartosc.length == 0) {
alert('Nie wpisałeś żadnej wartości!')
} else {
alert('Twój tekst zaczyna się od: ' + wartosc.charAt(0) );
}
Kolejny przykład pokazuje, jak możemy sprawdzić czy wpisywany ciąg ma odpowiednią formę. W przykładzie za pomocą wyrażeń regularnych sprawdzamy, czy użytkownik wpisał prawidłowe Imię:
<form method="post" action="...">
<fieldset>
<label for="ImieReg">Podaj imię:</label>
<input type="text" id="ImieReg" name="ImieReg" value="" />
</fieldset>
</form>
<script type="text/javascript">
document.getElementById('ImieReg').onchange = function() {
var imie = this.value;
var wzor = /^[a-zA-Z]+$/g
if (!wzor.test(imie)) {
alert("Co to za dziwne imię?...");
this.select();
}
}
</script>
Do naszego pola ImieReg podpieliśmy zdarzenie onchange, które zostanie wywołane w momencie zmiany wartości w tym polu.
Anonimowa funkcja która została przypisana do tego zdarzenia odwołuje się do tego pola tekstowego instrukcją this.
Wyrażenie regularne /^[a-z]+$/g sprawdza, czy tekst w całości składa się tylko z liter, a wiadomo przecież, że imienia innych znaków mieć nie mogą.
Dodatkowo znak "+" mówi nam, że wpisywany ciąg musi mieć jakąś długość (można by to zaostrzyć poprzez użycie konstrukcji np. {3,} tak by imiona miały minimalną długość 3 znaków, ale nie zawsze takie obostrzenia są właściwe).
Zastosowanie okienek alert jest bardzo proste w implementacji, jednak nie jest najbardziej przyjemną metodą informowania o błędnie wprowadzanych danych. Informację o złych danych lepiej wyświetlać w nieco bardziej subtelny sposób. Można np wyświetlać przy błędnie wypełnionym polu komunikat lub ikonkę mówiącą "źle zrobiłeś :)":
<input type="text" name="Imie" id="Imie" value="" /><span class="zleDane">źle</span>
//...
var imie = document.getElementById('ImieReg2');
var wzor = /^[a-zA-Z]+$/g
imie.onchange = function() {
var errorSpan = this.nextSibling; //pobieramy span leżący tuż za naszym polem
if (!wzor.test(imie.value)) {
errorSpan.style.display = "inline";
} else {
errorSpan.style.display = "none";
}
}
Wpisz w poniższy formularz jakieś błędne dane - np cyfry, po czym zaznacz przycisk Wyslij (np klawiszem Tab):
Logiczne wydaje się, by sprawdzać dane po wywołaniu zdarzenia onChange. W końcu chcemy sprawdzić czy nowa (czyli zmieniona) wartość jest właściwa. Jest jednak pewne małe ALE. Przeglądarki nie odpalają tego zdarzenia, jeżeli wprowadzaną wartość wybierzemy z auto uzupełnienia (czyli pola, które pojawia nam się przy wypełnianym polu i daje możliwość wyboru wcześniej wprowadzonych odpowiedzi). Dlatego też w chwili obecnej nie możemy do końca polegać na zdarzeniu onChange. Są z tego dwa wyjścia. Jedno - stosowanie zdarzenia onBlur (czyli - po odznaczeniu/opuszczeniu danego elementu - w tym przypadku musimy przerywać sprawdzanie, gdy w pole zostanie wprowadzony pusty ciąg znaków (czyli gdy użytkownik tylko przeszedł przez pole).), lub ustawienie danemu polu atrybutu autocomplete="false". Bardziej logicznym rozwiązaniem wydaje się to drugie, jednak wiąże się z nim wprowadzenie utrudnienia dla użytkownika.
Kolejne pole w naszym formularzu powinno zawierać prawidłowy e-mail. Aby sprawdzić tą wartość, wystarczy zastosować wyrażenie regularne, które opisuje wygląd prawidłowego maila:
document.getElementById('Mail').onchange = function() {
var WzorMaila = /^[0-9a-z_.-]+@[0-9a-z.-]+\.[a-z]{2,3}$/i
if (!WzorMaila.test(this.value)) {
alert("Błędny mail!");
} else {
alert('Twój mail to: ' + this.value);
}
}
Podobne techniki możemy stosować do większości typów danych - np do sprawdzania kodów pocztowych, miejscowości itp. Wystarczy odpowiednio opisywać takie dane za pomocą wyrażeń regularnych. Z drugiej strony nie powinniśmy też przesadzać z dokładnością sprawdzania takich danych. W końcu nie zawsze przeciętny użytkownik chce podawać dokładnie swoje dane. Czasami po prostu chcę sobie stworzyć konto "na szybko".
Przyciski radiowe umożliwiają wybór tylko jednej opcji z pośród kilku. Tak więc powinniśmy je stosować w przypadkach "albo". Albo lubię czekoladę, albo nie lubię ;).
Każde pole należące do jednej grupy powinno mieć wspólną nazwę, ale indywidualną wartość (no chyba, że w szczególnych przypadkach...).
<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>
function sprawdzRadio() {
var formularz = document.getElementById('Formularz');
var ile = 0;
for (x=0; x<formularz.plec.length; x++) {
if (formularz.plec[x].checked) {
alert('zaznaczyles opcję nr: ' + x);
break;
}
}
Jak nie trudno się domyślić, nasze guziki są uporządkowane w formie tablicy. Tak więc pierwszy guzik to plec[0], następny plec[1], a ostatni guzik to plec[plec.length-1]. Powyższa funkcja wykonuje pętlę po wszystkich przyciskach i w razie znalezienia zaznaczonego przycisku (checked) wyświetla stosowny komunikat.
Oczywiście można także wyświetlać wartość zaznaczonego pola:
for (x=0; x<formularz.plec.length; x++) {
if (formularz.plec[x].checked) {
alert('Wartość zaznaczonego pola: ' + plec[x].value)
break;
}
}
Jak sprawdzić czy cokolwiek zostało wybrane (powiedzmy, że nie ustawimy właściwości checkded dla żadnego pola)?
Wystarczy na samym początku funkcji ustawić dodatkową zmienną na -1. Następnie wykonując pętlę sprawdzamy, czy któreś pole jest zaznaczone. Jeżeli tak, naszej zmiennej przypisujemy numer zaznaczonego pola (czyli wartość od 0 do guziki_radio.length). Na przykład:
var ktory = -1;
var plec = document.getElementById('Formularz').plec
for (x=0; x<plec.length; x++) {
if (plec[x].checked) {
ktory = x;
break;
}
}
if (ktory == -1) {
alert('Nie zaznaczyles żadnego pola!');
}
<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>
Przyciski typu checkbox umożliwiają wybór równoczesnie kilku opcji.
Aby sprawdzić, czy dany checkbox jest zaznaczony, musimy odczytać jego właściwość checked, np:
if (document.getElementById('checkbox').checked) {
alert("zaznaczony!");
}
Tak samo jak w przypadku pól radio, także pola checkbox grupujemy w tablicę dzięki nadaniu wspólnej nazwy. Dzięki temu możemy operować na checkboxach jak na tablicy:
function sprawdz() {
var ileZaznaczonych = 0;
var formularz = this.form;
var checkboxy = formularz.kolor;
for (x=0; x<checkboxy.length; x++) {
if (checkboxy[x].checked) {
ileZaznaczonych++;
}
}
if (ileZaznaczonych!=2) {
alert('Wybrałeś ' +ileZaznaczonych+' owoce, a musisz wybrać 2');
} else {
alert('Wybrałeś ' +ileZaznaczonych+' owoce. Bądź pozdrowiony!');
}
}
Do formy w której znajduje się przycisk wywołujący funkcję odwołałem się poprzez instrukcję this.form. Dzięki temu skrypt jest jeszcze bardziej uniwersalny, ponieważ nie musimy pobierać nazwy formularza.
Jeżeli w nazwie zastosujemy kwadratowe nawiasy, wysłane przez formularz wartości naszych zaznaczonych pól będą dostępne w PHP w postaci tablicy:
<input type="checkbox" name="hobby[]" value="rower"> rower<br /> <input type="checkbox" name="hobby[]" value="kino"> kino<br /> <input type="checkbox" name="hobby[]" value="komputer"> komputer<br /> <input type="checkbox" name="hobby[]" value="rysowanie"> rysowanie<br />
W powyższym przykładzie odwołanie się poprzez nazwę (name) nie będzie możliwe. Metoda getElementById też odpada, gdyż nasze elementy musiały by mieć identyczne ID, co jest niedopuszczalne. Aby odwołać się do tak nazwanych elementów trzeba zastosować konstrukcję:
var tablicaHobby = document.Formularz.elements['hobby[]'];
Więcej o przypadku stosowania nawiasów kwadratowych w nazwach możesz dowiedzieć się tutaj.
<select name="kolorOczu"> <option value="-">wybierz kolor</option> <option value="niebieskie">niebieskie</option> <option value="zielone">zielone</option> <option value="bronzowe">bronzowe</option> <option value="czarne">czarne</option> </select>
Elementy option pola select są zgrupowane w tablicy która ma nazwę taką samą jak element select (w naszym przykładzie "kolorOczu").
var obiektSelect = document.getElementById('Formularz').kolorOczu;
var iloscElementow = obiektSelect.length
//lub
var obiektSelect = document.getElementById('Formularz').kolorOczu;
var iloscElementow = obiektSelect.options.length
Każdy z obiektów typu SELECT udostępnia nam tablicę options w której znajdują się kolejne pozycje (obiekty) option.
Aby sprawdzić który element został wybrany skorzystamy z właściwości selectedIndex:
var obiektSelect = document.getElementById('Formularz').kolorOczu;
alert(obiektSelect.selectedIndex); //wypisze numer wybranego indeksu
Aby sprawdzić wartość value wybranego elementu skorzystamy z instrukcji:
var obiektSelect = document.getElementById('Formularz').kolorOczu;
alert( obiektSelect.options[obiektSelect.selectedIndex].value );
Javascript daje także możliwość tworzenie nowych pól option. Tak samo jak przy tworzeniu nowych obiektów skorzystamy tutaj z instrukcji new:
var tekst = "zaznacz mnie";
var wartosc = "opcja";
var NowyOption = new Option(tekst, wartosc);
var obiektSelect = document.getElementById('Formularz').kolorOczu;
obiektSelect.options[x] = NowyOption;
//lub
document.getElementById('Formularz').kolorOczu.options[x] = new Option(tekst, wartosc, 0, 0);
Tworząc nowy obiekt option musimy podać dla niego właściwości:
Jeżeli dodamy element o indeksie który już istnieje, to stary element zostanie zastąpiony. Jeżeli dodamy element o indeksie nr 10, a lista ma ich tylko 5, to powstaną puste, niezdefiniowane pola, a na miejscu 10 nowy element (w FireFoxie można definiować tylko elementy które są kolejnymi elementami dla SELECTA).
Dodawanie nowych elementów - szczególnie z wykożystaniem pętli jest bardzo wygodną metodą tworzenia elementów typu select.
Przykład zastosowania pętli do konstruowania select możesz zobaczyć w formularzu wyboru daty.
Stwórzmy selecta, który będzie zawierał wybrane elementy innego selecta. Aby wykonać to zadanie, wykonujemy pętlę po elementach option pierwszego selecta. Gdy dany element option jest zaznaczony (selected), wówczas tworzymy w drugim selekcie nowy option, który ma tekst i value takie same jak dany element w pierszym selekcie.
<select id="pierwszy" multiple="multiple">
<option value="1"> opcja 1 </option>
<option value="2"> opcja 2 </option>
<option value="3"> opcja 3 </option>
</select>
<select id="drugi" multiple="multiple">
</select>
<input type="button" id="kopiuj" value="Kopiuj zaznaczone elementy option" />
<script type="text/javascript">
function przenies(skad, dokad) {
var select_pierwszy = document.getElementById(skad);
var select_drugi = document.getElementById(dokad);
if (select_pierwszy.length>0) {
for (var x=0; x<select_pierwszy.length; x++){
if (select_pierwszy.options[x].selected) {
var NowyOption = new Option(select_pierwszy.options[x].firstChild.nodeValue, select_pierwszy.options[x].value, false, false);
select_drugi[select_drugi.length] = NowyOption;
}
}
}
}
document.getElementById('kopiuj').onclick = function(){
przenies('pierwszy', 'drugi')
}
</script>
Standardowe przyciski typu submit i reset są mdłe, a ich stylowanie wcale do najłatwiejszych rzeczy nie należy. Najlepiej oczywiście nie ruszać wyglądu elementów formularza, czasami jednak nie mamy zbytnio takiej możliwości (szczególnie gdy grafik tworzący layout chciał udowodnić własne kompetencje :))
Za pomocą JavaScript możemy zrobić własnoręcznie takie guziki. Wystarczy, że wywołamy metodę submit() lub reset()
document.getElementById('buttonWyslij').onclick = function() {
var forularz = document.getElementById('Formularz');
if (wszystkie dane są ok) {
formularz.submit()
} else {
...jeżeli dane są nieprawidłowe...
}
}
document.getElementById('buttonReset').onclick = function() {
document.getElementById('Formularz').reset();
}
<form name="Formularz" method="post" action="skrypt_na_serwerze.php"> <fieldset> <legend>Guziczki</fieldset> <input type="image" src="obrazekGuzikaWyslij" id="buttonWyslij" /> <input type="image" src="obrazekGuzikaReset" id="buttonReset" /> </fieldset> </form>
Jak wiemy, zdarzenie onclick możemy podpiąć pod każdy element, dzięki czemu wysłanie formularza może się odbywać po kliknięciu dowolnego elementu.
Oradzam jednak takie podejście :). Tworząc przyciski wysyłania w taki sposób, zaburzamy semantykę strony (a przecież chcemy być dobrymi webmasterami).
Wysyłanie formularzy powinno się odbywać poprzez użycie przycisku Submit (lub naciśnięcie klawisza Enter), którego wygląd zmienimy za pomocą CSS. W praktyce w 99% przypadków się to sprawdza.