Praktyczne wprowadzenie do JavaScript #18

Wiele systemów blogowych, CMSów i innych narzędzi pracujących na dużej liczbie danych (konta pocztowe, notki na bloga i tak dalej) oferuje z reguły możliwość ich usunięcia. W przypadku małej liczby materiału, zadanie to nie stanowi dla użytkownika problemu. Kłopoty pojawiają się jednak, kiedy trzeba kompleksowo usunąć daną partię danych, a klikanie w pojedyncze linki jest mało ergonomiczne i po prostu uciążliwe. W tym wypadku z pomocą przychodzą tzw. czekboksy, czyli poczciwy input type=”checkbox”.

Screencast z tego odcinka znajdziecie tutaj, natomiast przykład na żywo tutaj.

Spójrzmy na ten przykład:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL">
<head>
<title>Strona z formularzem</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />

<style type="text/css">
form label
{
	display: block;
}
</style>
<!-- Nasz kod Javascript: -->

<script type="text/javascript">


</script>

</head>
<body>
	<div>
		<form name="formularz">
		<label for="all" style="margin-bottom: 20px;">
			zaznacz/odznacz wszystkie <input type="checkbox" id="all" />
		</label>
		
		<label for="jeden">pierwsza opcja <input type="checkbox" name="jeden" value="1" id="jeden" /></label>
		<label for="dwa">druga opcja<input type="checkbox" name="dwa" value="2" id="dwa" /></label>
		<label for="trzy">trzecia opcja<input type="checkbox" name="trzy" value="3" id="trzy" /></label>
		<input type="submit" value="usuń" />
		</form>
	</div>
</body>
</html>

Z całą pewnością znacznie łatwiej byłoby użyć jednego czekboksa o nazwie zaznacz/odznacz wszystkie, aniżeli klikać w pojedyncze opcje. Postaramy się osiągnąć ten efekt. Najpierw jednak omówmy logikę pisanego skryptu, który umożliwi podobne zaznaczanie wszystkich opcji.

Zacznijmy, naturalnie, od punktu pierwszego. Jak widać, trzeba dodać zdarzenie dla inputu. Wykorzystamy do tego poznaną wcześniej funkcję dodajZdarzenie, którą skonstruowaliśmy, by dodawać zdarzenia w zgodny ze standardami sposób. Mowa o tym kodzie:

	function dodajZdarzenie(odnosnik, zdarzenie, funkcja)
	{
		if (odnosnik.addEventListener)
		{
			odnosnik.addEventListener(zdarzenie, funkcja, false);
		}
		else
		{
			odnosnik.attachEvent("on"+zdarzenie, funkcja);
		}
	}

Mając już osadzoną powyższą funkcję w dokumencie HTML, pora dodać zdarzenie. Jak już wielokrotnie wspominałem, aby dodać zdarzenia dla elementów HTML, trzeba to zrobić po załadowaniu struktury dokumentu. Jako że funkcja o nazwie Laduj doda nam zdarzenie dla inputa, uruchomimy ją po załadowaniu dokumentu, używając zdarzenia onload:

dodajZdarzenie(window, "load", Laduj);

Pora teraz na samą funkcję Laduj, w której dodamy zdarzenie onclick dla input id="all".

	function Laduj()
	{
		var box = document.getElementById('all');
		dodajZdarzenie(box, "click", Zaznacz);
	}

Jej konstrukcja nie jest skomplikowana, ale z obowiązku przypomnę, że najpierw, w pierwszej linijce pobieramy odnośnik do inputu, który teraz przechowuje zmienna o nazwie box. Następnie uruchamiamy funkcję dodajZdarzenie, która dzięki przekazanym odpowiednim argumentom (box, "click", Zaznacz) dodaje inputowi zdarzenie onclick w postaci funkcji Zaznacz.

Dodając w ten sposób zdarzenia, robimy to w sposób najbardziej zgodny ze standardami. Gdybyśmy cofnęli się kilkanaście odcinków wstecz, zrobilibyśmy to pewnie w ten sposób:

<label for="all" style="margin-bottom: 20px;">
			zaznacz wszystkie <input type="checkbox" id="all" onclick="Zaznacz()" />
		</label>

Albo tak:

	function Laduj()
	{
		var box = document.getElementById('all');
		box.onclick = Zaznacz;
	}

Jeśli uporaliśmy się ze zdarzeniem onclick, należy teraz zaimplementować funkcję Zaznacz, która będzie wisienką na naszym torcie. Jak ustaliliśmy, funkcja ta będzie między innymi rozpoznawać, czy zaznaczyć elementy, czy też je odznaczyć. Informację o tym przechowywać będzie zmienna o nazwie done. Dodajmy więc ją do skryptu, zanim zaczniemy bawić się z funkcją Zaznacz.

<script type="text/javascript">

	var done = false;

Jak widać, dodaliśmy ją na początku, co jest dobrym nawykiem - wszystkie zmienne, które powinny być dostępne dla wszystkich funkcji (lub mogą być, ale nie jesteśmy tego pewni), deklarujemy na początku.

Nie pozostaje nam teraz nic innego, jak zająć się wreszcie funkcją Zaznacz.

Na początku pobierzmy więc wszystkie elementy formularza:

	function Zaznacz()
	{
		var elements = document.forms['formularz'].elements;

Następnie skorzystajmy z pętli for by mieć dostęp do wszystkich pobranych wyżej elementów po kolei:

		for (var i = 0;i<elements.length ;i++ )
		{

Następnie, dla świętego spokoju, ustalmy, że chcemy działać tylko na inputach o type="checkbox". Jeśli więc type danego elementu będzie inny, pętla pominie ten element, w przeciwnym razie zaznaczy go bądź odznaczy.

	function Zaznacz()
	{
		var elements = document.forms['formularz'].elements;
		
		for (var i = 0;i<elements.length ;i++ )
		{
			if (elements[i].type == "checkbox")
			{

Analizując ostatnią linijkę stwierdzamy, że pętla będzie się dalej wykonywać, jeśli zostanie spełniony warunek elements[i].type == "checkbox", czyli parametr type będzie równy checkbox. Jednym słowem - tak, jak zamierzaliśmy.

Jako że input id="all" jest także zawarty w formularzu, co za tym idzie pobraliśmy go do zmiennej elements, należy też wykluczyć sytuację, że będziemy go odznaczać bądź zaznaczać, jak inne inputy. Pomoże nam w tym uzupełnienie warunku o sprawdzenie identyfikatora danego elementu. Jeśli więc pętla natrafi na input o id równym all, pominie go.

	function Zaznacz()
	{
		var elements = document.forms['formularz'].elements;
		
		for (var i = 0;i<elements.length ;i++ )
		{
			if (elements[i].type == "checkbox" && elements[i].id != "all")
			{

Teraz wystarczy zająć się zaznaczeniem inputów:

	function Zaznacz()
	{
		var elements = document.forms['formularz'].elements;
		
		for (var i = 0;i<elements.length ;i++ )
		{
			if (elements[i].type == "checkbox" && elements[i].id != "all")
			{
				if (!done)
				{
					elements[i].checked = "true";
				}
				else
				{
					elements[i].checked = "";
				}
			}

Instrukcje warunkowe, jakie dodaliśmy, możemy przetłumaczyć mniej więcej tak:

Jeśli zmienna done ma wartość false, zaznaczmy element, inaczej odznaczmy go.

Zobaczmy teraz co się dzieje. Mamy następujący dokument HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL">
<head>
<title>Strona z formularzem</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />

<style type="text/css">
form label
{
	display: block;
}
</style>
<!-- Nasz kod Javascript: -->

<script type="text/javascript">

	var done = false;
	function dodajZdarzenie(odnosnik, zdarzenie, funkcja)
	{
		if (odnosnik.addEventListener)
		{
			odnosnik.addEventListener(zdarzenie, funkcja, false);
		}
		else
		{
			odnosnik.attachEvent("on"+zdarzenie, funkcja);
		}
	}
	function Laduj()
	{
		var box = document.getElementById('all');
		dodajZdarzenie(box, "click", Zaznacz);
	}

	function Zaznacz()
	{
		var elements = document.forms['formularz'].elements;
		
		for (var i = 0;i<elements.length ;i++ )
		{
			if (elements[i].type == "checkbox" && elements[i].id != "all")
			{
				if (!done)
				{
					elements[i].checked = "true";
				}
				else
				{
					elements[i].checked = "";
				}
			}
		}
	}

	dodajZdarzenie(window, "load", Laduj);

</script>

</head>
<body>
	<div>
		<form name="formularz">
		<label for="all" style="margin-bottom: 20px;">
			zaznacz wszystkie <input type="checkbox" id="all" />
		</label>
		
		<label for="jeden">pierwsza opcja <input type="checkbox" name="jeden" value="1" id="jeden" /></label>
		<label for="dwa">druga opcja<input type="checkbox" name="dwa" value="2" id="dwa" /></label>
		<label for="trzy">trzecia opcja<input type="checkbox" name="trzy" value="3" id="trzy" /></label>
		<input type="submit" value="usuń" />
		</form>
	</div>
</body>
</html>

Testując skrypt dochodzimy do wniosku, że działa tylko zaznaczenie inputów. Dlaczego więc po drugim kliknięciu nie można ich odznaczyć?

Sedno sprawy leży w wartości zmiennej done. Popatrzmy na fragment funkcji Zaznacz:

if (!done)
{
	elements[i].checked = "true";
}
else
{
	elements[i].checked = "";
}

Jak widać, każdy input odznaczy się, kiedy wartość done będzie true. Tu pojawia się jednak problem, bo domyślnie zmienna ta ma wartość false, co zapisaliśmy tutaj:

var done = false;

i nigdzie więcej się nie zmienia. Tak więc instrukcja warunkowa będzie zawsze spełniona, a wszystko to, co jest po else { nigdy nie wykona się. Trzeba więc, po wykonaniu funkcji Zaznacz zaktualizować wartość zmiennej.


if (!done)
{
	done = true;
}
else
{
	done = false;
}

Voila! Tym razem wszystko działa w najlepsze! Popatrzmy:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL">
<head>
<title>Strona z formularzem</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />

<style type="text/css">
form label
{
	display: block;
}
</style>
<!-- Nasz kod Javascript: -->

<script type="text/javascript">

	var done = false;
	function dodajZdarzenie(odnosnik, zdarzenie, funkcja)
	{
		if (odnosnik.addEventListener)
		{
			odnosnik.addEventListener(zdarzenie, funkcja, false);
		}
		else
		{
			odnosnik.attachEvent("on"+zdarzenie, funkcja);
		}
	}
	function Laduj()
	{
		var box = document.getElementById('all');
		dodajZdarzenie(box, "click", Zaznacz);
	}

	function Zaznacz()
	{
		var elements = document.forms['formularz'].elements;
		
		for (var i = 0;i<elements.length ;i++ )
		{
			if (elements[i].type == "checkbox" && elements[i].id != "all")
			{
				if (!done)
				{
					elements[i].checked = "true";
				}
				else
				{
					elements[i].checked = "";
				}
			}
		}
		if (!done)
		{
			done = true;
		}
		else
		{
			done = false;
		}
	}

	dodajZdarzenie(window, "load", Laduj);

</script>

</head>
<body>
	<div>
		<form name="formularz">
		<label for="all" style="margin-bottom: 20px;">
			zaznacz wszystkie <input type="checkbox" id="all" />
		</label>
		
		<label for="jeden">pierwsza opcja <input type="checkbox" name="jeden" value="1" id="jeden" /></label>
		<label for="dwa">druga opcja<input type="checkbox" name="dwa" value="2" id="dwa" /></label>
		<label for="trzy">trzecia opcja<input type="checkbox" name="trzy" value="3" id="trzy" /></label>
		<input type="submit" value="usuń" />
		</form>
	</div>
</body>
</html>

Komentarze

1

No wreszcie wróciłeś :) Czekałem niecierpliwie i aż się nauczylem jQuery. Teraz mam dylemat, czy warto wogóle zagłebiać się w „czysty” JS?
Bo… hmm… czyż to nie jest piękne?


$(document).ready(function(){
	$("#all").click(function() {
		$("input[@type='checkbox']").not("#all").each(
			function(){
				this.checked=!this.checked
			});
	});
});

czy jak kto woli:


$(document).ready(function(){$("#all").click(function() {$("input[@type='checkbox']").not("#all").each(function(){this.checked=!this.checked});});});
wzs
2

Wiesz, zadnym frameworkiem wszystkiego nie zrobisz i zawsze przyda sie dodatkowa wiedza. Poza tym, mnie by tam ciekawilo, dlaczego, chocby w frameworku, dzieje sie tak czy tak ;)

jQ jest swietny, jesli chodzi o manipulowanie DOM na rozne sposoby i mysle, ze to dobry kierunek.

3

@wzs: Wszystko pięknie fajnie, tyle tylko, że to jest praktyczne wprowadzenie do JavaScriptu a nie praktyczne zastosowania jQuery :> Trzeba liznąć czystego JS żeby zrozumieć jQ.

Andrzej
4

Ja wiem, że to na potrzeby przykładu, ale gdyby ktoś chciał od razu posiąść arkana JavaScriptu (ale mi się powiedziało :D) to podpowiem, że checkbox zaznaczający/odznaczający wszystkie inne powinien być dodany nieinwazyjnie do dokumentu. :-)

Wzs: jQuery jest świetne, ale trzeba wręcz znać mechanizmy stojące za frameworkami. Inaczej operujesz na czymś magicznym i jak się popsuje to „WTF” i nie ruszysz dalej. ;>

5

Riddle: przeciez jest, widac, ze przejrzales kurs pobieznie, bo wyraznie napisalem tylko, ze zrobilibysmy to inwazyjnie, gdybysmy nie znali lepszych metod. Do kąta! ;)

Pozdrawiam :-)

6

wsz: a odpal ten swój kod i zobacz co robi :]

blue
7

Panowie, ja tu nie chcę „swoich” rozwiązań forsować na siłę. Akurat mi bardzo podoba się styl pisania Damiana i bede śledził ten kurs do końca. Na ten odcinek czekałem tak długo, że aż zacząłem szukać czegoś innego- a akurat jQuery jakoś dawno chodziło mi po głowie. Zaznaczam, że wcześniej JS znałem tyle co się tutaj nauczyłem- ale po dość krótkiej lekturze tutoriali i dokumentacji jQuery na ten przykład z checkboxami spojrzałem z lekko ironicznym uśmieszkiem ;) Czy trzeba znać JS aby posługiwać się frameworkiem? IMHO zależnie od potrzeb, ale do prostych czynności wystarczą podstawy programistyczne+manual.

@blue: jak dołaczysz plik jquery.js to działa :) chyba ze mi tu coś wciął WP. I nie jestem tym marnym raperem ;) wzs

wzs
8

Działać działa, ale nie tak jak powinno ;)
Za literówkę przepraszam – jakoś tak palce same złożyły się do „sz” :P

A z jQuery i wszelkimi tego typu bilbiotekami jest taki problem, że kod wcale nie jest krótszy i wydajniejszy, bo tak na prawdę ładujesz człowiekowi na stronie 20kB skryptu, żeby zaznaczyć wszystkie checkboxy. Imvho trzeba zawsze się zastanowić, czy nie lepiej dla prostego dzialania, które chcemy wykonać, trochę sie pomęczyć i znaleźć (o ile oczywiście js nie znamy) gotowe rozwiązanie w czystym js, zamiast niepotrzebnie obciążać agenta użytkownika.

blue
9

Mam następujący przykład
input type="checkbox" name="id[]" id="id1"
input type="checkbox" name="id[]" id="id2"

Bo jak każedmu wiadomo PHP nie pójdzie bez tablicy w name a js nie pójdzie z tablicą, więc trzeba by było dać ID. Jest wszystko ok jeżeli ktoś nie dba o swój kod.
Jeżeli ktoś dba to wie, że każe ID musi być unikalne i trzeba je inkrementować co później sprawia kłopoty z jego przetwarzaniem.

Dla laików może być to nielada problem, chyba że robią stronę w czystym HTMLu :)

Adrian
10

To wydaje mi się bardziej uniwersalne, ze względu na fakt że nie zawsze będziemy wywoływali metody bezparametrowe:

function dodajZdarzenie(odnosnik, zdarzenie, funkcja) {
if (odnosnik.addEventListener) {
odnosnik.addEventListener(zdarzenie, function() { funkcja }, false);
}
else {
odnosnik.attachEvent(„on” + zdarzenie, function() { funkcja });
}
}
dodajZdarzenie(window, „load”, alert(‚test’));

pozdr,

Dodaj komentarz

Dozwolone tagi: <blockquote>, <code>, <strong>