Praktyczne wprowadzenie do JavaScript #17

Siedemnasty odcinek naszej nauki przebiegał będzie pod dyktando jak naprawić to, co popsuli programiści Internet Explorer?. Ponadto nauczymy się, jak dynamicznie zmieniać wersje kolorystyczne naszej strony.

Przypomnijmy, że skończyliśmy nasze zmagania z tym oto dokumentem 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" />

<!-- Nasz kod Javascript: -->

<script type="text/javascript">

	var active;

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

	function Sprawdz()
	{
		if (this.value.length == this.getAttribute('maxlength'))
		{
			active = this.name;
			Next();
		}

	}
	function Next()
	{
		elements = document.forms['formularz'].elements;
		for (var i = 0;i < elements.length ; i++ )
		{
			if (elements[i].name == active)
			{	if (elements[i+1])
				{
					elements[i+1].focus();
				}

			}
		}
	}
	function Laduj()
	{
		elements = document.forms['formularz'].elements;
		for (var i = 0;i < elements.length ; i++ )
		{

			if (elements[i].getAttribute('maxlength') < 50)
			{
				dodajZdarzenie(elements[i], "keyup", Sprawdz);
			}

		}
	}

	dodajZdarzenie(window, "load", Laduj);

</script>

</head>
<body>
	<div>
		<form name="formularz">
		  <input type="text" name="jeden" maxlength="5" />
		  <input type="text" name="dwa" maxlength="5"  />
		  <input type="text" name="jeden2" maxlength="5" />
		  <input type="text" name="dwa2" maxlength="5"  />
		</form>
	</div>
</body>
</html>

Jak zapewne sobie przypominasz, problemem był brak dostępu do zmiennej this wewnątrz funkcji. Oczywiście tylko w Internet Explorer. Sprawa jest jednak do załatwienia, otóż musimy dostać się do this poprzez inną konstrukcję. Umożliwia nam to jedna z właściwości okna przeglądarki (pamiętne window), lecz pamiętaj, tylko w IE.

Z pomocą przychodzi konstrukcja window.event, która użyta wewnątrz funkcji, obsługującej dowolne zdarzenie, zawiera wszystkie jego właściwości. Idąc tym tropem możemy np. otrzymać nazwę zdarzenia, do którego przypisaliśmy jakąś funkcję. Popatrzmy:

<head>
<title>Strona z zdarzeniem</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />

<!-- Nasz kod Javascript: -->

<script type="text/javascript">

	window.onload = function()
	{
		document.getElementById('link').onclick = Info;
	}
	function Info()
	{
		alert(window.event.type);
	}

</script>

</head>
<body>
	<a href="#" id="link">klik!</a>
</body>
</html>

Do naszej dyspozycji mamy jeden link w dokumencie, któremu nadaliśmy identyfikator id=”link”. Postanowiliśmy, że kiedy klikniemy na niego, pojawi się nam okienko, które wyświetli jakie zdarzenie zostało wywołane (konkretnie jego nazwę). Odpowiada za to, jak widać:

alert(window.event.type);

Dowiadujemy się, że type jest jedną z właściwości window.event, w tym przypadku oznacza ona nazwę zdarzenia.

Wykorzystując właściwości window.event możemy także, co istotne, odwołać się do elementu, na którym wykonywane jest zdarzenie. Mowa tutaj o właściwości srcElement, którą zastosujemy, aby znaleźć alternatywę dla nie działającego this.

Aby upewnić się, że to działa, podstawmy do poprzedniego przykładu z type, srcElement odwołując się do zawartości linka w innerHTML:

function Info()
{
	alert(window.event.srcElement.innerHTML);
}

Jak widać, pojawi się nam klik!, czyli zawartość pomiędzy tagami <a id=”link”></a>. Przejdźmy teraz do naszego przykładu z poprzedniego odcinka.

Wadliwą funkcją, którą trzeba naprawić, aby skrypt działał pod Internet Explorer, jest funkcja Sprawdz. Przyjrzyjmy się jej bliżej:

function Sprawdz()
{
	if (this.value.length == this.getAttribute('maxlength'))
	{
		active = this.name;
		Next();
	}

}

Jeśli dobrze policzyłem, zawiera ona trzy razy użytą zmienną this. Trzeba ją, jak już powiedzieliśmy, czymś zamienić, tyle że jedynie w przypadku, kiedy użytkownik używa IE. Podobny problem mieliśmy w poprzednim odcinku, kiedy przyszło nam użyć funkcji addEventListener bądź attachEvent. I problem ten rozwiążemy podobnie, jak tam, używając sprytnie konstrukcji if.

function Sprawdz()
{
	if (window.event && window.event.srcElement)
	{
		var el = window.event.srcElement;
	}
	else
	{
		var el = this;
	}
	if (this.value.length == this.getAttribute('maxlength'))
	{
		active = this.name;
		Next();
	}

}

Po poprawkach, funkcja wygląda teraz tak, jak powyżej. Jak widać, sprawdziliśmy (if (window.event && window.event.srcElement), czy przeglądarka, jakiej używamy, obsługuje konstrukcje w nawiasie. IE da odpowiedź pozytywną, więc do zmiennej el zapisujemy wartość window.event.srcElement, która, przypomnijmy, przechowuje odnośnik do elementu, na którym wykonywane jest zdarzenie w postaci funkcji Sprawdz. Wracając do przykładu, w przeciwnym razie, kiedy przeglądarka nie wie co to jest window.event (na przykład Mozilla), przypisujemy do naszej zmiennej this. Pora teraz podstawić, jak powiedzieliśmy, w trzech miejscach zamiast this zmienną el:

function Sprawdz()
{
	if (window.event && window.event.srcElement)
	{
		var el = window.event.srcElement;
	}
	else
	{
		var el = this;
	}
	if (el.value.length == el.getAttribute('maxlength'))
	{
		active = el.name;
		Next();
	}

}

Sprawdźmy teraz cały skrypt:

<!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" />

<!-- Nasz kod Javascript: -->

<script type="text/javascript">

	var active;

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

	function Sprawdz()
	{
		if (window.event && window.event.srcElement)
		{
			var el = window.event.srcElement;
		}
		else
		{
			var el = this;
		}

		if (el.value.length == el.getAttribute('maxlength'))
		{
			active = el.name;
			Next();
		}

	}
	function Next()
	{
		elements = document.forms['formularz'].elements;
		for (var i = 0;i < elements.length ; i++ )
		{
			if (elements[i].name == active)
			{	if (elements[i+1])
				{
					elements[i+1].focus();
				}

			}
		}
	}
	function Laduj()
	{
		elements = document.forms['formularz'].elements;
		for (var i = 0;i < elements.length ; i++ )
		{

			if (elements[i].getAttribute('maxlength') < 50)
			{
				dodajZdarzenie(elements[i], "keyup", Sprawdz);
			}

		}
	}

	dodajZdarzenie(window, "load", Laduj);

</script>

</head>
<body>
	<div>
		<form name="formularz">
		  <input type="text" name="jeden" maxlength="5" />
		  <input type="text" name="dwa" maxlength="5"  />
		  <input type="text" name="jeden2" maxlength="5" />
		  <input type="text" name="dwa2" maxlength="5"  />
		</form>
	</div>
</body>
</html>

Wszystko działa w najlepszym porządku!

Zmiana wersji kolorystycznej strony

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

Często spotykamy się z pomysłem, aby uprzyjemnić życie odwiedzającym nasze strony i dać im do wyboru kilka wersji kolorystycznych danego site’u. Tego typu rozwiązania używa choćby Last.FM. System ten wykorzystuje arkusze stylów CSS, gdzie zapisane są wszystkie definicje każdej ze skórek. Istnieje jednak kilka sposobów na uzyskanie tego efektu, z których wybierzemy dwa:

My zajmiemy się na początku drugim przykładem, gdyż pierwszy wiąże się kolejny raz z inną implementacją funkcji w IE, potrzebnych nam do dostania się do zawartości <link>.

Na początek dodajmy linki, które będą odpowiedzialne za zmianę skórki:

<body id="czarny">
	<div>
		<h1>Cos tam</h1>
	</div>
	<a href="#red" id="red" class="czerwony">czerwona skórka</a>
	<a href="#black" id="black" class="czarny">czarna skórka</a>

</body>
</html>

Linki te mają stosowne idenfytikatory, tak, abyśmy mogli się do nich dostać w łatwy sposób, kiedy będziemy chcieli nadać im stosowne zdarzenia. Klasy linków na razie zostaną pominięte milczeniem, aczkolwiek wkrótce do nich wrócimy, bo są niezwykle potrzebne.

Działanie naszego skryptu jest proste. Po kliknięciu w link czerwona bądź czarna skórka skrypt ma zmienić identyfikator znacznika <body>. Realizować to zadanie będzie, dajmy na to, funkcja o nazwie ZmienKolor. Należy więc najpierw przypisać do każdego z linków zdarzenie onclick w postaci właśnie tej funkcji:


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

	function Laduj()
	{
		document.getElementById('red').onclick = ZmienKolor;
		document.getElementById('black').onclick = ZmienKolor;
	}

	function ZmienKolor()
	{
	}

	dodajZdarzenie(window, "load", Laduj);

Szanując dobre nawyki, zastosowaliśmy tutaj dodawanie zdarzeń poprzez funkcje addEventListener bądź attachEvent. W wyniku naszych działań, po załadowaniu dokumentu, uruchamia się funkcja Laduj, która nadaje zdarzenia onclick naszym linkom. Warto się jej przyjrzeć, z jednego powodu:

	function Laduj()
	{
		document.getElementById('red').onclick = ZmienKolor;
		document.getElementById('black').onclick = ZmienKolor;
	}

Jak widać użyliśmy tutaj właściwości onclick. Dlaczego? Z czystej wygody, gdyż równie dobrze można zastosować w tym wypadku funkcję dodajZdarzenie. Wyglądałoby to tak:

	function Laduj()
	{
		dodajZdarzenie(document.getElementById('red'), "click", ZmienKolor);
		dodajZdarzenie(document.getElementById('black'), "click", ZmienKolor);
	}

Pozostało nam zaimplementować funkcję ZmienKolor.

Jak sobie pewnie przypominasz, nasze linki posiadają również atrybut class. Jego wartość to w jednym wypadku czerwony, a w drugim czarny, a właśnie takie identyfikatory powinien mieć znacznik <body>, aby zmieniła się wersja kolorystyczna. Trzeba więc dostać się do elementu na który kliknęliśmy w funkcji ZmienKolor i wyciągnąć jego class. Następnie wystarczy tylko podstawić go do wartości identyfikatora body.

	function ZmienKolor()
	{
		if (window.event && window.event.srcElement)
		{
			var el = window.event.srcElement;
		}
		else
		{
			var el = this;
		}

		document.body.id = el.className;
	}

Jako że w IE znów nie będziemy mogli użyć this by dostać się do elementu, na którym nastąpiło wywołanie zdarzenia, pomagamy sobie konstrukcją, którą przed chwilą poznaliśmy. Oto w zmiennej el znajduje się odwołanie do this (dla IE oczywiście window.event.srcElement). W ten sposób możemy pobrać właściwość className, która oznacza nazwę przypisanej klasy do elementu. Zmiana id body to tylko formalność:

document.body.id = el.className;

Warto jednak wspomnieć, dlaczego wybraliśmy el.className, a nie el.getAttribute('class');. Tak, to drugie nie zadziała w Internet Explorer...

Cały dokument wygląda następująco:

<!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 wersjami kolorystycznymi</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<style type="text/css">
	#czerwony div
	{
		background: #000;
	}
	#czerwony div h1
	{
		color: #C00000;
	}

	#czarny div
	{
		background: #C00000;
	}
	#czarny div h1
	{
		color: #000;
	}
</style>

<!-- Nasz kod Javascript: -->

<script type="text/javascript">

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

	function Laduj()
	{
		dodajZdarzenie(document.getElementById('red'), "click", ZmienKolor);
		dodajZdarzenie(document.getElementById('black'), "click", ZmienKolor);
	}

	function ZmienKolor()
	{
		if (window.event && window.event.srcElement)
		{
			var el =  window.event.srcElement;
		}
		else
		{
			var el = this;
		}

		document.body.id = el.className;
	}

	dodajZdarzenie(window, "load", Laduj);
</script>
	

</head>
<body id="czarny">
	<div>
		<h1>Cos tam</h1>
	</div>
	<a href="#red" id="red" class="czerwony">czerwona skórka</a>
	<a href="#black" id="black" class="czarny">czarna skórka</a>
</body>
</html>

Komentarze

1

ferrante to moj mistrz ;p

frodomirc
2

Frodomir, Ty jeszcze zyjesz? :P

3

zyje ;p czemu nie wlazisz na irc? Polnet -> kanal #monty_python ;p
I nie manipuluj komentarzami, geju ;p

frodomirc
4

No i przebrnąłem po jednej nocy i południu przec cały kurs, co zresztą sobie obiecałem już przy Twoim pierwszym wpisie– dopiero teraz znalazłem czas :)

Bardzo przyjemnie czyta się Twoj kurs, zobaczymy na ile przygotował do codziennych zmagan z JS`em ;)
W każdym razie, teraz to i łatwiej będzie zapoznać się z cięższymi publikacjami- bo dotychczas nowoczesny JS to dla mnie była magia.

Powodzenia w pisaniu dalej.

PS. Taki mały tips- masz coś o frameworkach JS w planach?

wzs
5

wzs – dzieki za pozytywne opinie :-)

Co do frameworkow, i ten temat zostanie poruszony (szczegolnie gdy przerobimy AJAX’a). Ogolnie rzecz biorac caly kurs ruszy znow z kopyta, kiedy niebawem skoncze dlugo zapowiadany projekt…

Pozdrawiam

6

poradnik ma już kawał czasu i IE zdążył pójść już do przodu, najnowsza wersja radzi sobie ze zmienną this

barmic
7

Tak, radzi sobie ze zmienną this, jak i addEventListener. Ale nie każdy używa nowej wersji przeglądarki i niestety jak na razie dalej trzeba stosować te „sztuczki”.

Hustek
8

takie może dziwne pytanie, ale dlaczego w

function ZmienKolor()
{
if (window.event && window.event.srcElement)
{
var el = window.event.srcElement;
}
else
{
var el = this;
}
document.body.id = el.className;
}

poza klamrami if i else zmienna (a właściwie zmienne) el jest widoczna, przecież nie była zdefiniowana nigdzie wyżej, więc powinna istnieć tylko w obszarze objętym klamrami czy jedno el w if i drugie el w else. tymczasem spokojnie mogę go sobie użyć poza warunkiem…

Dodaj komentarz

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