<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ferrante.pl - simplicity of web programming &#187; Vademecum</title>
	<atom:link href="http://ferrante.pl/category/tech/vademecum/feed/" rel="self" type="application/rss+xml" />
	<link>http://ferrante.pl</link>
	<description>Technologie internetowe, PHP5, Python, Javascript. Publicystyka i kursy w najlepszym wydaniu.</description>
	<lastBuildDate>Tue, 27 Jul 2010 19:28:32 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>varJS.com &#8211; zaawansowany JavaScript za darmo!</title>
		<link>http://ferrante.pl/2010/04/15/varjs-com-zaawansowany-javascript-za-darmo/</link>
		<comments>http://ferrante.pl/2010/04/15/varjs-com-zaawansowany-javascript-za-darmo/#comments</comments>
		<pubDate>Thu, 15 Apr 2010 20:24:11 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Vademecum]]></category>

		<guid isPermaLink="false">http://ferrante.pl/?p=745</guid>
		<description><![CDATA[To nie koniec niespodzianek w tym tygodniu! Uruchomiłem właśnie stronę varJS.com, gdzie wkrótce powstanie blog poświęcony JavaScript i pochodnym. Tym razem po angielsku. Mam zamiar publikować tam także kolejne rozdziały mojej książki o JavaScript zupełnie za darmo. Być może będzie to funkcjonowało na zasadzie wiki, tego jeszcze nie ustaliłem.
Co jednak dzisiaj istotne, wydałem prawie 250-stronicowy, [...]]]></description>
			<content:encoded><![CDATA[<p>To nie koniec niespodzianek w tym tygodniu! Uruchomiłem właśnie stronę <a href="http://varjs.com">varJS.com</a>, gdzie wkrótce powstanie blog poświęcony JavaScript i pochodnym. Tym razem po angielsku. Mam zamiar publikować tam także kolejne rozdziały mojej książki o JavaScript zupełnie za darmo. Być może będzie to funkcjonowało na zasadzie wiki, tego jeszcze nie ustaliłem.</p>
<p>Co jednak dzisiaj istotne, wydałem prawie 250-stronicowy, długo oczekiwany materiał PDF ze szkoleń i mojego dotychczasowego researchu o nazwie <span class="f">Just Advanced JavaScript</span>. Książka, a w zasadzie slajdy mają charakter ściągawki &#8211; opisana została lwia część języka oraz mnóstwo ciekawostek i haczyków. Wszystko znajdziecie pod <a href="http://varjs.com">varJS.com</a> &#8211; serdecznie zapraszam! Prawdopodobnie wkrótce rozszerzę to o kolejne rozdziały i linki do innych pożytecznych stron, które również możecie tam spotkać.</p>
<p>Niniejszym dziękuje <a href="http://kukawski.pl">Rafałowi Kukawskiemu</a> oraz <a href="http://leaverou.me">Lei Verou</a>, którzy odpowiadali za korektę i poprawę ewentualnych błędów merytorycznych. Z całego serca polecam współpracę z nimi, prawdziwi eksperci!</p>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2010/04/15/varjs-com-zaawansowany-javascript-za-darmo/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>&#8220;this&#8221; w JavaScript</title>
		<link>http://ferrante.pl/2010/02/02/this-w-javascript/</link>
		<comments>http://ferrante.pl/2010/02/02/this-w-javascript/#comments</comments>
		<pubDate>Tue, 02 Feb 2010 21:36:51 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Vademecum]]></category>

		<guid isPermaLink="false">http://ferrante.pl/?p=672</guid>
		<description><![CDATA[Niedawno przyznałem, że na podstawie szkolenia pojawią się nowe artykuły. Czasem będą one kopiować jego treść, a innym razem ją rozszerzą, czy dopowiedzą to, co zostało z różnych przyczyn pominięte/uproszczone. Czas na pierwszy temat, czyli co tak naprawdę oznacza mityczne słówko this w JavaScript.
this występuje w blokach funkcyjnych bądź w globalnym scope, jak zresztą zdążyliście [...]]]></description>
			<content:encoded><![CDATA[<p>Niedawno przyznałem, że na podstawie szkolenia pojawią się nowe artykuły. Czasem będą one kopiować jego treść, a innym razem ją rozszerzą, czy dopowiedzą to, co zostało z różnych przyczyn pominięte/uproszczone. Czas na pierwszy temat, czyli co tak naprawdę oznacza mityczne słówko <span class="f">this</span> w JavaScript.<span id="more-672"></span></p>
<p><strong>this</strong> występuje w blokach funkcyjnych bądź w globalnym <em>scope</em>, jak zresztą zdążyliście już pewnie nieraz zauważyć. Ciężko tak naprawdę powiedzieć, co dokładnie oznacza. Chyba najlepszą definicją będzie to, że <em>this</em> wskazuje na jakiś obiekt w zależności od <em>kontekstu</em>, w jakim została użyta funkcja, w której go zdefiniowaliśmy. Pisząc kontekst mam nam myśli głównie sposób i miejsce wywołania.</p>
<h3>Obiekt globalny</h3>
<p>O JavaScript w przeglądarkach wiemy, że obiekt globalny wskazuje na <span class="f">window</span>. Tym samym <span class="f">this</span> użyty w globalnym kontekście będzie wskazywał także na <em>window</em>:</p>
<pre><code>&lt;script type="text/javascript"&gt;
	window === this; <strong>// true</strong>
&lt;/script&gt;</code></pre>
<p>W ten sposób z łatwością możemy dojść do wniosku, że poniższa funkcja użyta w globalnym <em>scope </em> zawsze będzie zwracała obiekt globalny:</p>
<pre><code>&lt;script type="text/javascript"&gt;
	var global = (function() { return this; })();
&lt;/script&gt;</code></pre>
<h3>Konstruktory a funkcje</h3>
<p>Zobaczmy, co dzieje się, kiedy zdefiniujemy jakąś funkcję, która dodaje pewną własność do obiektu, na który będzie wskazywał <em>this</em>:</p>
<pre><code>var fn = function() {
	this.foobar = "foo";
};</code></pre>
<p>Mając do dyspozycji coś takiego, jak powyżej, możemy naciąć się na dwa zupełnie różne zachowania. Możemy przecież napisać potem tak:</p>
<pre><code>var fn = function() {
	this.foobar = "foo";
};
var obj = new fn;</code></pre>
<p>Lub tak:</p>
<pre><code>var fn = function() {
	this.foobar = "foo";
};
fn();</code></pre>
<p>W pierwszym przypadku tworzymy instancję nowego obiektu na podstawie konstruktora <span class="f">fn</span>. Jak możemy się domyślić, w ten sposób <em>this</em> będzie wskazywał na nowoutworzony obiekt (<span class="f">obj</span>) i będziemy mogli użyć czegoś podobnego:</p>
<pre><code>var fn = function() {
	this.foobar = "foo";
};
var obj = new fn;
obj.foobar; <strong>// "foo"</strong></code></pre>
<p>W drugim listingu nie korzystamy jednak z operatora <span class="f">new</span>, co za tym idzie nie tworzymy bezpośrednio nowego obiektu na podstawie konstruktora. Uruchamiamy po prostu funkcję jak każdą inną. Po wykonaniu się tego kodu nie dostajemy błędu, tak że możemy być pewni, że wszystko poszło dobrze i jest on &#8211; przynajmniej pod względem składni &#8211; absolutnie poprawny. Co więc dzieje się tutaj?:</p>
<pre><code>fn();</code></pre>
<p>JavaScript w takich przypadkach domyślnie przekierowuje na samą górę scope i <strong>this</strong> będzie wskazywał na obiekt globalny. W ten sposób pod <em>this</em> będzie ukryta referencja do <em>window</em> i będziemy mogli użyć zmiennej globalnej <em>foobar</em> tak:</p>
<pre><code>window.foobar; <strong>// "foo"</strong></code></pre>
<p>Bądź tak:</p>
<pre><code>window['foobar']; <strong>// "foo"</strong></code></pre>
<p>A także tak, jak poniżej, jako że każdy składnik obiektu <em>window</em> jest zmienną globalną:</p>
<pre><code>foobar; <strong>// "foo"</strong></code></pre>
<p>Warto także powiedzieć, że funkcje zdefiniowane w innych konstruktorach/funkcjach nie dziedziczą <em>this</em> z funkcji wyżej, tylko także odwołują się do globalnego obiektu:</p>
<pre><code>var zewnetrzna = function() {
	return function wewnetrzna() {
		return this;
	};
};

var fn = new zewnetrzna();
fn(); <strong>// window</strong></code></pre>
<h3>Object literals</h3>
<p>Ważny jest, jak powiedziałem, kontekst. Definiując sobie obiekt w postaci <em>object literal</em>, <span class="f">this</span> odwołuje się domyślnie do obiektu, w którym został utworzony:</p>
<pre><code>var obj = {
	bar: function() { return this; }
};</code></pre>
<p>Pisząc więc coś takiego:</p>
<pre><code>obj.bar();</code></pre>
<p><em>this</em> wskaże na <em>obj</em>. Przy okazji udowodnijmy kolejny raz, że funkcje wewnętrzne nie dziedziczą <em>this</em>:</p>
<pre><code>var obj = {
	bar: function() {
		console.log(this);
		return function() {
			console.log(this);
			return this;
		}
	}
};</code></pre>
<p>Szybki test potwierdza naszą tezę:</p>
<pre><code>obj.bar()();</code></pre>
<p>Choć pamiętajmy, że równie dobrze można zapisać tak:</p>
<pre><code>var fn = obj.bar();
fn();</code></pre>
<p>Wszystko wygląda tak dlatego, że JavaScript wspiera zwracanie funkcji, które możemy potem uruchomić dodając nawiasy wywołania.</p>
<p>Zobaczymy teraz bardzo ciekawą rzecz, związaną z kontekstem. Zdefiniujmy najpierw <em>object literal</em>:</p>
<pre><code>var obj = {
	bar: function() {
		return this;
	}
};</code></pre>
<p>Uruchamiając to tak:</p>
<pre><code>obj.bar(); <strong>// obj</strong></code></pre>
<p><em>this</em> zwraca <em>obj</em>. Jeśli jednak literalnie zmienimy to, co będzie po kropce, kontekst będzie już inny:</p>
<pre><code>var f = obj.bar;
f(); <strong>// window</strong></code></pre>
<p>Dlaczego tak?</p>
<p>Jako że zmienne globalne możemy pisać tak:</p>
<pre><code>window['globalna'] = "cos";
window['globalna']; // "cos"</code></pre>
<p>Równie dobrze możemy zrobić jak poniżej, co wyjaśni nam, dlaczego <em>this</em> wskazuje na <em>window</em>:</p>
<pre><code>window['f'] = obj.bar;
window['f'](); <strong>// window</strong></code></pre>
<p>Mamy więc po lewej obiekt <em>window</em>, czyli funkcja <strong>f</strong> jest uruchamiana właśnie w jego kontekście i to do niego <em>this</em> będzie odnosił.</p>
<p>Przeanalizujcie też poniższy kod, który wynikł z inicjatywy <a href="http://blog.kukawski.pl">Rafała Kukawskiego</a> w trakcie rozmowy ze mną:</p>
<pre><code>var fn = function() {
	var b = function(){
		return this;
	};
	this.foo = function() {
		return b();
	}
};
var obj = new fn();
obj.foo(); // window</code></pre>
<p><strong>Inne metody</strong></p>
<p><em>this</em> można również zmienić poprzez metody <em>call</em> i <em>apply</em> wywołane na konstruktorach, ale to temat na kolejny artykuł.</p>
<p>Na koniec ćwiczenie ze szkolenia &#8211; co zwróci następujący kod i dlaczego:</p>
<pre><code>var e = !!3;
var fn = function() {
	return {
		foobar: this.e,
		e: !!0
	};
};

var res = fn();
res.foobar; // ?</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2010/02/02/this-w-javascript/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Falsy values i operatory porównania</title>
		<link>http://ferrante.pl/2009/09/05/falsy-values-i-operatory-porownania/</link>
		<comments>http://ferrante.pl/2009/09/05/falsy-values-i-operatory-porownania/#comments</comments>
		<pubDate>Sat, 05 Sep 2009 16:22:15 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Vademecum]]></category>

		<guid isPermaLink="false">http://ferrante.pl/?p=596</guid>
		<description><![CDATA[Jest coś takiego w JavaScript, co się zowie jak powyżej. Niestety, bardzo często jest to temat niezrozumiały. Miałem okazję uczestniczyć bądź po prostu układać pytania do kilku rekrutacji na JavaScript developera w dużych firmach i muszę stwierdzić, że czasem bywa wręcz z falsy values dramatycznie.
O co takiego chodzi? Falsy values1 to nic innego, jak zbiór [...]]]></description>
			<content:encoded><![CDATA[<p>Jest coś takiego w JavaScript, co się zowie jak powyżej. Niestety, bardzo często jest to temat niezrozumiały. Miałem okazję uczestniczyć bądź po prostu układać pytania do kilku rekrutacji na JavaScript developera w dużych firmach i muszę stwierdzić, że czasem bywa wręcz z falsy values dramatycznie.<span id="more-596"></span></p>
<p>O co takiego chodzi? <span class="f">Falsy values</span><a href="#przypis-1" class="hint"><sup>1</sup></a> to nic innego, jak zbiór wartości, które dają fałsz. Coś w ten deseń:</p>
<pre><code>if (!value) {
	console.log('Jedną z falsy values jest: '+ value);
}</code></pre>
<p>Pytanie, jakie są to wartości? Oto one:</p>
<ul>
<li>&#8220;&#8221; &#8211; pusta zmienna typu String</li>
<li>0 &#8211; typu Number</li>
<li>false typu Boolean</li>
<li>undefined</li>
<li>null</li>
<li>NaN typu Number</li>
</ul>
<p>Czyli:</p>
<pre><code>if (!NaN) {
	console.log('Jedną z falsy values jest: '+ NaN);
}</code></pre>
<pre><code>if (!null) {
	console.log('Jedną z falsy values jest: '+ null);
}</code></pre>
<p>Wszystkie te wartości przy konwertowaniu na typ Boolean (np. <span class="f">!!NaN</span>) dają <span class="f">false</span>. Warto zapamiętać!</p>
<h3>Operatory porównania w JavaScript</h3>
<p>Jeśli mowa o <em>falsy values</em> ciekawe rzeczy dzieją się przy ich porównywaniu. Jak wiadomo, mamy operatory <span class="f">==</span> i <span class="f">===</span>. Mnóstwo ludzi wykłada się na tym, banalnym zresztą (po głębszej analizie) problemie.</p>
<p>Warto zapamiętać, że pierwszy operator porównuje jedynie wartości, drugi &#8211; wartości oraz typy. Poniższe przykłady dają więc <span class="f">prawdę</span>:</p>
<pre><code>"0" == 0
"    " == 0
[] == 0
undefined == null
false == new String("")</code></pre>
<p><span class="f">Fałsz</span> zwracają natomiast podobne konstrukcje:</p>
<pre><code>NaN == 0
null == 0
undefined == ""
0 == {}</code></pre>
<p>Kiedy idzie o operator ścisłego porównania robi się ciekawiej &#8211; JavaScript oprócz logicznej wartości porównuje również typy zmiennych (a więc czy są obiektem, stringiem, liczbą itd.). <span class="f">Prawdę</span> zwracają:</p>
<pre><code>0 === 0
"" === ""
[] !== [] // dla kontrastu..</code></pre>
<p><span class="f">Fałsz</span> natomiast poniższe przykłady:</p>
<pre><code>"" === 0 // typ string i number
0 === null // typ number i object
[] === 0 // typ object i number
[] === "    " // typ object i string</code></pre>
<p>Warto więc być ostrożnym w trakcie pisania aplikacji w JavaScript.</p>
<hr />
<ol id="notes">
<li id="przypis-1">Temat falsy values poruszył na przykład Douglas Crockford w swojej znakomitej książce &#8220;JavaScript: The Good Parts&#8221;. Polecam!</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2009/09/05/falsy-values-i-operatory-porownania/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>Singleton w JavaScript</title>
		<link>http://ferrante.pl/2009/07/09/singleton-w-javascript/</link>
		<comments>http://ferrante.pl/2009/07/09/singleton-w-javascript/#comments</comments>
		<pubDate>Wed, 08 Jul 2009 23:09:36 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Vademecum]]></category>

		<guid isPermaLink="false">http://ferrante.pl/?p=541</guid>
		<description><![CDATA[Ostatnio, wspólnie z Markiem Stasikowskim, myśleliśmy intensywnie &#8211; w ramach zabawy z JavaScript &#8211; nad jak najlepszą implementacją wzorca projektowego Singleton. Wspólnymi siłami doszliśmy do dość fajnego kodu, który pozwolę sobie przedstawić i nieco omówić.
Przypomnijmy, o co tak naprawdę chodzi we wzorcu Singleton. Ujmując rzecz najprościej, Singleton ma za zadanie udostępniać tylko jedną instancję danej [...]]]></description>
			<content:encoded><![CDATA[<p>Ostatnio, wspólnie z <a href="http://www.goldenline.pl/marek-stasikowski2">Markiem Stasikowskim</a>, myśleliśmy intensywnie &#8211; w ramach zabawy z JavaScript &#8211; nad jak najlepszą implementacją wzorca projektowego Singleton. Wspólnymi siłami doszliśmy do dość fajnego kodu, który pozwolę sobie przedstawić i nieco omówić.<span id="more-541"></span></p>
<p>Przypomnijmy, o co tak naprawdę chodzi we wzorcu Singleton. Ujmując rzecz najprościej, Singleton ma za zadanie udostępniać tylko jedną instancję danej klasy w obrębie całej aplikacji. Używając tego wzorca mamy pewność, że zawsze operujemy na tym samym obiekcie, a pamięć nie zostanie zbyt zaśmiecona. Praktyczną implementację Singletonu można zastosować na przykład do klasy XHR (XMLHttpRequest) czy też rozpoznającej wersję przeglądarki internetowej.</p>
<p>Co jeszcze charakterystyczne dla tego wzorca, to prywatny konstruktor. W efekcie wywołanie <span class="f">new Singleton()</span> jest niemożliwe. Jedyną instancję klasy Singleton pobieramy specjalnie do tego stworzoną metodą statyczną, np. <span class="f">Singleton.getInstance()</span>.</p>
<p>Spójrzmy więc na najprostszą implementację wzorca. Zakładamy, ze klasą typu Singleton będzie tak naprawdę klasa (funkcja) <em>Constructor</em>.</p>
<pre><code>var Singleton = (function(){
	var Constructor = function() {
		this.data = 'some data';
}	

	return {
		getInstance: function (){
			return this.instance || (this.instance = new Constructor);
		}
	}
})();</code></pre>
<p>Co tutaj najważniejsze, to natychmiastowe utworzenie obiektu z funkcji anonimowej<a href="#przypis-1" class="hint"><sup>1</sup></a>:</p>
<pre><code>var Singleton = (function() { ... })();</code></pre>
<p>Uniemożliwia to stworzenie czegoś na wzór <span class="f">new Singleton()</span>, ponieważ do zmiennej <em>Singleton</em> zostanie przypisany rezultat natychmiastowego uruchomienia (ściślej mówiąc: skonkretyzowania obiektu) powyższej funkcji, a nie jej ciało.</p>
<p>Następnie tworzymy prywatny konstruktor: </p>
<pre><code>var Constructor = function(){
	this.data = 'some data';
}</code></pre>
<p>Ostatnie, co zostało do zrobienia, to zwrócenie jakieś konkretnej wartości wywoływanej anonimowej funkcji, tak by zmienna <em>Singleton</em> mogła używać metody <em>getInstance</em> (<em>Singleton.getInstance()</em>).</p>
<pre><code>return {
	getInstance: function (){
		return this.instance || (this.instance = new Constructor);
	}
}</code></pre>
<p>W ciele funkcji <em>getInstance</em> zwracamy instancję klasy <em>Constructor</em> (która, jak założyliśmy, ma być Singletonem), jeśli już jest stworzona lub tworzony ją, zapisując ją przy okazji jako składową, którą będziemy zwracać przy kolejnych wywołaniach <em>getInstance</em>. Wypada zrobić testy:</p>
<pre><code>var foo = Singleton.getInstance();
var bar = Singleton.getInstance();

console.log(foo === bar); // true

foo.data = 'foobar';
console.log(bar.data); // foobar</code></pre>
<p>Dzięki <span class="f">===</span> dokonujemy sprawdzenia, czy wybrane obiektu są tego samego typu, a potem, czy zmiana dokonana na jednym obiekcie typu Singleton, powoduje zmiany w pozostałych obiektach. Działa!</p>
<h3>Update</h3>
<p>Podany w powyższej postaci Singleton można niestety nadpisać. Nie mamy więc pewności, że obiekt Singletonu będzie cały czas dostępny. Jednym z prostszych rozwiązań jest użycie <span class="f">const</span>, która definiuje w JavaScript stałe:</p>
<pre><code>const Singleton = (function(){
	var Constructor = function() {
		this.data = 'some data';
}	

	return {
		getInstance: function (){
			return this.instance || (this.instance = new Constructor);
		}
	}
})();</code></pre>
<p>W ten sposób próba przypisania <span class="f">Singleton = &#8220;cos tam&#8221;</span> jest niemożliwa. Niestety rozwiązanie to nie działa na IE, ale programujemy na poważne przeglądarki, czyż nie?</p>
<p>Warto też mieć świadomość, że w przypadku obecnej implementacji, zmienna <em>instance</em> jest publiczna (czyli możemy użyć <em>Singleton.instance</em>). Możemy ją ukryć w prosty sposób, używając closures:</p>
<pre><code>const Singleton = (function(){
	var Constructor = function() {
		this.data = 'some data';
}, instance = null;	

	return {
		getInstance: function (){
			return instance || (instance = new Constructor);
		}
	}
})();</code></pre>
<hr />
<ol id="notes">
<li id="przypis-1">(function() {})() można też zapisać jako new function() {}</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2009/07/09/singleton-w-javascript/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>jQuery &#8211; to łatwe #6: Piszemy prostą grę!</title>
		<link>http://ferrante.pl/2008/06/05/jquery-to-latwe-6-piszemy-prosta-gre/</link>
		<comments>http://ferrante.pl/2008/06/05/jquery-to-latwe-6-piszemy-prosta-gre/#comments</comments>
		<pubDate>Thu, 05 Jun 2008 16:44:49 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Vademecum]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://ferrante.pl/2008/06/05/jquery-to-latwe-6-piszemy-prosta-gre/</guid>
		<description><![CDATA[Bardzo ciekawym eksperymentem, jeśli chodzi o JavaScript jest stworzenie jakiejkolwiek gry w tym języku. Pomaga to przede wszystkim nauczyć się podejścia koncepcyjnego do tworzenia tego typu kodu. Gdy wszystko oprzemy o jQuery, może być to niezwykle ekscytujące doświadczenie, jeśli chodzi o poznawanie tego frameworka. Do dzieła!


Spróbujmy zrobić grę, polegającą na tym, że w danym obszarze [...]]]></description>
			<content:encoded><![CDATA[<p>Bardzo ciekawym eksperymentem, jeśli chodzi o JavaScript jest stworzenie jakiejkolwiek gry w tym języku. Pomaga to przede wszystkim nauczyć się podejścia koncepcyjnego do tworzenia tego typu kodu. Gdy wszystko oprzemy o jQuery, może być to niezwykle ekscytujące doświadczenie, jeśli chodzi o poznawanie tego frameworka. Do dzieła!</p>
<p class="p"><a href='http://ferrante.pl/wp-content/uploads/2008/06/gra_js.jpg' title='gra w js'><img src='http://ferrante.pl/wp-content/uploads/2008/06/gra_js.jpg' alt='gra w js' /></a></p>
<p><span id="more-101"></span></p>
<p>Spróbujmy zrobić grę, polegającą na tym, że w danym obszarze poruszać będą się kolorowe boksy/skrzynki/cokolwiek, które należy usunąć, klikając w dany element. Za każdy udany klik i zniszczenie kolorowego prostokąta otrzymujemy jeden punkt. Zadanie jest proste &#8211; ustrzelić jak najwięcej boksów w danym czasie. Mimo że gra nie jest zbyt odkrywcza i na pewno znudzi się po kilku minutach, stanowi dobrą podstawę do dalszego tworzenia tego typu aplikacji.</p>
<p>Całość będzie toczyć się w obszarze <span class="f">#area</span>, który będzie zwykłym <em>divem</em>. O takim:</p>
<p class="p" style="width: 507px;"><a href='http://ferrante.pl/wp-content/uploads/2008/06/gra_area.jpg' title='gra_area.jpg'><img src='http://ferrante.pl/wp-content/uploads/2008/06/gra_area.jpg' alt='gra_area.jpg' /></a></p>
<pre><code>&lt;div id="area"&gt;&lt;/div&gt;</code></pre>
<p>Po wystartowaniu gry będziemy dodawać do tego <em>diva</em> kolejne boksy, które trzeba będzie trafić kursorem myszki. Wtedy kod HTML będzie wyglądać mniej wiecej tak:</p>
<pre><code>&lt;div id="area"&gt;
	&lt;div id="losowe_id"&gt;&lt;/div&gt;
	&lt;div id="losowe_id"&gt;&lt;/div&gt;
	&lt;div id="losowe_id"&gt;&lt;/div&gt;
&lt;/div&gt;</code></pre>
<p>Obok obszaru, na którym toczyć będzie się ta pasjonująca rozgrywka, widniał będzie nasz aktualny wynik i licznik czasu, który pozostał do końca:</p>
<pre><code>&lt;div id="score"&gt;

	&lt;h3&gt;Wynik:&lt;/h3&gt;&lt;h4 id="result"&gt;0&lt;/h4&gt;

	&lt;div&gt;&lt;h3&gt;Pozostało:&lt;/h3&gt;&lt;h4&gt;&lt;span id="time"&gt;30&lt;/span&gt; sekund&lt;/h4&gt;&lt;/div&gt;
&lt;/div&gt;</code></pre>
<p>Powstały szablon HTML gry prezentuje się następująco:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
 &lt;head&gt;
  &lt;title&gt;Gra&lt;/title&gt;
  &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
  &lt;script type="text/javascript" src="jquery.js"&gt;&lt;/script&gt;

  &lt;script type="text/javascript"&gt;

	//MIEJSCE NA NASZ KOD

  &lt;/script&gt;
  &lt;style type="text/css"&gt;
	* { margin: 0; padding: 0; }
	#wrapper
	{
		margin-left: 200px;
		width: 650px;
	}
	#area
	{
		width: 500px;
		height: 400px;
		background: #F2F2F2;
		border: 1px solid #6B8180;
		float: left;
	}
	#area div { position: absolute; }
	#score
	{
		width: 100px;
		font-size: 15px;
		font-family: Trebuchet MS;
		float: left;
		margin-left: 20px;
	}
	h3, h4 { display: block; }
  &lt;/style&gt;
 &lt;/head&gt;

 &lt;body&gt;
 &lt;div id="wrapper"&gt;
	&lt;div id="area"&gt;&lt;/div&gt;
	&lt;div id="score"&gt;

		&lt;h3&gt;Wynik:&lt;/h3&gt;&lt;h4 id="result"&gt;0&lt;/h4&gt;

		&lt;h3&gt;Pozostało:&lt;/h3&gt;&lt;h4&gt;&lt;span id="time"&gt;30&lt;/span&gt; sekund&lt;/h4&gt;
	&lt;/div&gt;

 &lt;/div&gt;
 &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Dobrze, <a href="http://ferrante.pl/examples/jquery/jquery6/gra1.html">mamy już HTML</a> i jQuery na pokładzie. Przemyślmy teraz logikę działania gry.</p>
<p>Włączamy grę, wpisując adres do przeglądarki. Pora na działanie skryptu:</p>
<ul>
<li>Po załadowaniu DOM pobieramy do specjalnych zmiennych wielkość i położenie <span class="f">#area</span>, abyśmy wiedzieli, w jakim obszarze będą poruszać się nasze skrzynki. Następnie dodajemy do <span class="f">#area</span> kilka nowych <em>divów</em>. Losujemy wartości ich atrybutów: <em>height</em>, <em>width</em>, <em>left</em> i <em>top</em> (dwie ostatnie z uwagi na to, że boksy będą się ruszać, tak więc nieuchronne jest ustawienie <em>position:absolute</em> i następnie odpowiednia zmiana wartości <em>left</em> i <em>top</em>, tak by symulować ruch). Byłoby też fajnie, gdyby elementy te były różnego koloru. Musimy więc losowo wygenerować odpowiedni <em>background-color</em>. Oprócz tego, ustalamy <em>id</em> <em>diva</em>, tworząc je na podstawie daty utworzenia i wylosowanej wysokości elementu. Mamy w ten sposób 100% pewność, że żadne <em>id</em> się nie powtórzy. Ostatnią cechą, którą przydzielimy losowo, jest kierunek, w jakim poruszać będą się boksy. Zakładamy, że do wyboru mamy kierunki NW, NE, SW, SE. Gdy wszystko będzie gotowe, dodamy boksom zdarzenia <em>onclick</em>. Jeśli ktoś kliknie w element &#8211; ten zniknie, do wyniku zostanie doliczony jeden punkt, a także zostanie wygenerowanych od 1 do 3 nowych boksów.</li>
<li>Po dodaniu pierwszych &#8220;skrzynek&#8221;, musimy wprawić je w ruch. Dzięki <span class="f">window.setInterval</span> odpalimy co 200milisekund specjalną funkcję, która zbada położenie każdej skrzynki, a potem, w zależności od jej kierunku poruszania, odejmie bądź doda kilka pikseli wartościom stylów: <em>left</em> i <em>top</em>. Używając <em>setInterval</em> wprawimy w ruch także licznik czasu. Co sekundę zostanie on uaktualniony przez specjalną funkcję. Jeśli minie limit, funkcja ta przerwie poruszanie się klocków i zatrzyma grę.</li>
</ul>
<p>Do napisania gry zastosujemy styl <a href="http://en.wikipedia.org/wiki/CamelCase">Camel Case</a>, który, stosowany przede wszystkim w języku JAVA, jest bardzo przejrzysty. Oto zmienne, które nam posłużą w budowie gry:</p>
<ul>
<li><span class="f">boxMaxWidth</span>, <span class="f">boxMaxHeight</span>, <span class="f">boxMinWidth</span>, <span class="f">boxMinHeight</span> &#8211; maksymalne i minimalne wymiary boksu</li>
<li><span class="f">boxBackgrounds</span> &#8211; tablica z dostępnymi kolorami dla boksu</li>
<li><span class="f">boxContainer</span> &#8211; tablica skupiająca wszystkie boksy</li>
<li><span class="f">boxStartingCount</span> &#8211; początkowa liczba boksów</li>
<li><span class="f">boxAdditionalMaxCount</span> &#8211; maksymalna liczba boksów losowana po &#8220;ustrzeleniu&#8221; któregoś z nich</li>
<li><span class="f">boxMovement</span> &#8211; odnośnik do funkcji <em>moveBox</em>, uruchamianej cyklicznie</li>
<li><span class="f">boxMovementHorizontal</span>, <span class="f">boxMovementVertical</span> &#8211; liczba pikseli w ruchu poziomym i pionowym</li>
<li><span class="f">areaPosition</span> &#8211; obiekt, przechowujący pozycje pola gry</li>
<li><span class="f">areaSize</span> &#8211; obiekt, przechowujący wymiary pola gry</li>
<li><span class="f">gameTimer</span> &#8211; odnośnik do funkcji <em>executeTimer</em>, wywoływanej cyklicznie</li>
<li><span class="f">gameOver</span> &#8211; true/false, informuje, czy gra działa, czy jest skończona</li>
</ul>
<p>Poniżej z kolei funkcje, które musimy zaimplementować:</p>
<ul>
<li><span class="f">createBoxes(i)</span> &#8211; tworzy <strong>i</strong> elementów (boksów) w obszarze <em>#area</em>.</p>
<li><span class="f">setBoxRandomSize(obj)</span> &#8211; losuje wartości <em>height</em> i <em>width</em> dla obiektu DOM jQuery <em>obj</em>. Zwraca wylosowane liczby.</li>
<li><span class="f">setBoxRandomPosition(obj)</span> &#8211; losuje pozycje boksu (obiekt DOM jQuery) i zwraca uzyskane wartości.</li>
<li><span class="f">setBoxRandomBackground(obj)</span> &#8211; losuje kolor tła dla obiektu DOM jQuery <em>obj</em>.</li>
<li><span class="f">executeTimer()</span> &#8211; odpowiada za licznik czasu gry.</li>
<li><span class="f">moveBoxes()</span> &#8211; wprawia w ruch każdy z elementów.</li>
</ul>
<p>Jeśli mamy listę potrzebnych zmiennych i funkcji, wstawmy je w odpowiednie miejsce, by z czasem uzupełniać to i owo:</p>
<pre><code>	var boxMaxWidth = boxMaxHeight = 40;
	var boxMinWidth = boxMinHeight = 30;

	var boxBackgrounds = ["green", "red", "blue", "black", "pink"];

	var boxContainer = [];

	var boxStartingCount = 5;
	var boxAdditionalMaxCount = 3;

	var boxMovement;
	var boxMovementHorizontal = boxMovementVertical = 10;

	var areaPosition = {};
	var areaSize = {};

	var gameTimer;
	var gameOver = false;

	function createBoxes(i)
	{
	}
	function setBoxRandomSize(obj)
	{
	}
	function setBoxRandomPosition(obj)
	{
	}
	function setBoxRandomBackground(obj)
	{
	}
	function executeTimer()
	{
	}
	function moveBoxes()
	{
	}
	$().ready(function()
	{
	});</code></pre>
<p>Warto omówić sobie, kilka zastosowanych tutaj zabiegów. Po pierwsze:</p>
<pre><code>var boxMaxWidth = boxMaxHeight = 40;</code></pre>
<p>Jest to wielokrotne przypisanie wartości, używane zamiast czegoś takiego:</p>
<pre><code>var boxMaxWidth = 40; var boxMaxHeight = 40;</code></pre>
<p>Natomiast coś takiego:</p>
<pre><code>var areaSize = {};</code></pre>
<p>oznacza, że <em>areaSize</em> jest pustym obiektem. Z kolei</p>
<pre><code>var boxContainer = [];</code></pre>
<p>tworzy ze zmiennej <em>boxContainer</em> zwykłą tablicę.</p>
<p>Podsumowując, po włączeniu gry będziemy mieć do dyspozycji 5 boksów, których szerokość i wysokość mieścić będą się w przedziale od 30 do 40 pikseli. Dostępnych jest też 5 kolorów: &#8220;green&#8221;, &#8220;red&#8221;, &#8220;blue&#8221;, &#8220;black&#8221;, &#8220;pink&#8221;. Po strąceniu klocka skrypt będzie generował od 1 do 3 dodatkowych elementów. Co 200 milisekund każda &#8220;skrzynka&#8221; poruszać będzie się o 10 pikseli w poziomie i pionie. Pora teraz na to, co tygryski lubią najbardziej &#8211; kodowanie.</p>
<p>Ustaliliśmy, że po załadowaniu drzewa DOM, przeglądarka powinna pobrać dane na temat wymiarów i pozycji <span class="f">#area</span>, uruchomić dodawanie boksów oraz wprawić nowe elementy w ruch, by na końcu długiej listy czynności uruchomić licznik. Zajmijmy się więc tym wszystkim w pierwszej kolejności.</p>
<pre><code>$().ready(function()
{
	areaPosition = $("#area").position();
	areaSize.w = $("#area").width();
	areaSize.h = $("#area").height();

	createBoxes(boxStartingCount);

	boxMovement = window.setInterval("moveBoxes()", 200);
	gameTimer = window.setInterval("executeTimer()", 1000);

});</code></pre>
<p>Pierwsze 3 linijki to czyste jQuery &#8211; funkcje frameworka &#8211; <em>position</em>, <em>width</em> i <em>height</em> pozwalają nam pobrać stosowne informacje o <span class="f">#area</span>. Warto zwrócić uwagę na dwie rzeczy &#8211; <em>position()</em> zwraca obiekt o dwóch właściwościach: <em>left</em> i <em>top</em>. Dlatego też <em>areaPosition</em> będzie przechowywała te dwie wartości, a odwoływać się będziemy do nich tak:</p>
<pre><code>areaPosition.left; areaPosition.top;</code></pre>
<p>Podobnie jest z <em>areaSize</em> &#8211; jest to obiekt, któremu przypisujemy dwie, nowe właściwości &#8211; <em>h</em> i <em>w</em>, oznaczające wysokość i szerokość <span class="f">#area</span>.</p>
<p>Potem odpalamy <em>createBoxes</em> z argumentem <em>boxStartingCount</em>, która doda 5 nowych elementów (bo właśnie tyle przypisaliśmy zmiennej <em>boxStartingCount</em>.</p>
<p>Na końcu uruchamiamy odpowiednie funkcje, o których już mówiliśmy, z pewnym, cyklicznym opóźnieniem. Wszystko dzięki <em>setInterval</em>, o którym pisałem choćby <a href="http://ferrante.pl/2007/06/26/praktyczne-wprowadzenie-do-javascript-19/">tutaj</a>. </p>
<p>Zajmijmy się teraz pierwszą z brzegu &#8211; <em>createBoxes</em>.</p>
<p>Musimy stworzyć tyle boksów, ile podano w argumencie, tak więc użyjemy do tego pętli <em>for</em>, korzystającej z argumentu funkcji:</p>
<pre><code>function createBoxes(i)
{

	for (var x = 0;x < <strong>parseInt(i)</strong>; x++)
	{</code></pre>
<p>Następnie tworzymy nowego <em>diva</em>:</p>
<pre><code>function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		<strong>var div = $("&lt;div&gt;&lt;/div&gt;");</strong></code></pre>
<p>Pora na małą przerwę w naszych, nie wątpie, zdecydowanych działaniach. Po stworzeniu diva przychodzi czas na wygenerowanie jego właściwości. Omówmy więc, na czym polega uzyskiwanie losowych wartości w JavaScript.</p>
<p>Za całe zamieszanie odpowiedzialna jest funkcja <em>random()</em>, należąca do obiektu <em>Math</em> &#8211; <span class="f">Math.random()</span>. Losuje ona liczbę od 0 do 1 (przedział lewostronnie domknięty <0;1)) w postaci ułamkowej, tak więc najczęstsze jej wyniki to <em>0,24325325325</em>, <em>0,355554343</em> i tak dalej. Sama w sobie, funkcja <em>random</em> nie daje nam oczekiwanych rezultatów. Co zrobić, jeśli chcemy wylosować np. liczbę od 0 do 100? Odpowiedź jest prosta, musimy pomnożyć wynik funkcji <em>random</em> przez maksymalny wynik, jaki nas interesuje plus jeden. </p>
<pre><code>var int = Math.random()*101;</code></pre>
<p>W ten sposób <a href="http://ferrante.pl/examples/jquery/jquery6/gra2.html">osiągniemy</a> liczby z zakresu od 0 do 100, w formie 22,232323 itd. Jeśli chcemy tylko i wyłącznie liczby całkowite, należy uzyć funkcji <span class="f">Math.floor</span>. <a href="http://ferrante.pl/examples/jquery/jquery6/gra3.html">Zamienia ona</a> dowolne liczby na całkowite właśnie, zaokrąglając do dołu.</p>
<pre><code>var int = Math.floor(Math.random()*101);</code></pre>
<p>Pytanie, co zrobić, jeśli chcemy liczbę z zakresu np. 25 do 50. Jest to dość skomplikowane, jednak po chwili zastanowienia stwierdzamy, że do całości należy dodać minimum, a wylosowaną liczbę pomnożyć przez powiększoną o jeden różnicę między maksimum a minimum.</p>
<pre><code>var int = 25+Math.floor(Math.random()*26);</code></pre>
<p>Dla fanów matematyki zrozumienie <a href="http://ferrante.pl/examples/jquery/jquery6/gra4.html">powyższego przykładu</a> powinno być przyjemnością.</p>
<p>Bogatsi o podstawy teoretyczne, powróćmy do naszego przykładu. Musimy wylosować wysokość i szerokość boksu: </p>
<pre><code>function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("&lt;div&gt;&lt;/div&gt;");

		<strong>var sizes = setBoxRandomSize(div);</strong></code></pre>
<p>Uzupełnijmy więc funkcję <em>setBoxRandomSize</em>:</p>
<pre><code>function setBoxRandomSize(obj)
{
	var height = Math.floor((boxMaxHeight-boxMinHeight+1)*Math.random())+boxMinHeight;
	var width = Math.floor((boxMaxWidth-boxMinWidth+1)*Math.random())+boxMinWidth;

	obj.css({"height": height, "width": width});

	return {"height": height, "width": width};
}</code></pre>
<p>Sprawa jest prosta &#8211; losujemy wartości z zakresu 30-40 i ustawiamy, dzięki jQuery, odpowiednie wartości. Tym razem robimy to kompleksowo, zamiast </p>
<pre><code>obj.css("height", height).css("width", width);</code></pre>
<p>piszemy</p>
<pre><code>obj.css({"height": height, "width": width});</code></pre>
<p>Jest to notacja obiektowa, o której szerzej w następnych artykułach na tym blogu. Sens działania tego zapisu można zrozumieć jednak bez większych problemów. Jako argument funkcji <em>css</em> podajemy wartości wg poniższego szablonu:</p>
<pre><code>{nazwa_stylu: wartość, nazwa_stylu: wartosc, ..., nazwa_stylu: wartosc}</code></pre>
<p>Pora na wylosowanie losowej pozycji elementu:</p>
<pre><code>function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("&lt;div&gt;&lt;/div&gt;");

		var sizes = setBoxRandomSize(div);
		<strong>setBoxRandomPosition(div);</strong></code></pre>
<p>I uzupełnienie funkcji <em>setBoxRandomPosition</em>:</p>
<pre><code>function setBoxRandomPosition(obj)
{
	var left = Math.floor((areaSize.w-parseInt(obj.css("width")))*Math.random())+areaPosition.left;
	var top = Math.floor((areaSize.h-parseInt(obj.css("height")))*Math.random())+areaPosition.top;

	obj.css({"left": left +"px", "top": top +"px"});
}</code></pre>
<p>Następne losujemy tło:</p>
<pre><code>function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("&lt;div&gt;&lt;/div&gt;");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		<strong>setBoxRandomBackground(div);</strong></code></pre>
<p>W ten sposób:</p>
<pre><code>function setBoxRandomBackground(obj)
{
	var background = Math.floor(Math.random()*boxBackgrounds.length);
	obj.css("background-color", boxBackgrounds[background]);
}</code></pre>
<p>Do zmiennej <em>background</em> losujemy liczbę z zakresu od 0 do *liczba elementów tablicy <em>boxBackgrounds</em>* (wyrażona poprzez <em>boxBackgrounds.length</em>). W ten sposób mamy losowy klucz tablicy <em>boxBackgrounds</em>, zawierający któryś, z kolorów (<em>boxBackgrounds[background]</em>).</p>
<p>I tak, mamy uzupełnione 3 funkcje, generujące losowe dane. Musimy teraz dokończyć dzieło tworzenia funkcji <em>createBoxes</em>. Zacznijmy od wygenerowania losowego <em>id</em>:</p>
<pre><code>function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("&lt;div&gt;&lt;/div&gt;");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		<strong>var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);</strong></code></pre>
<p>Do zmiennej <em>date</em> tworzymy obiekt typu <em>Date</em>, który jest w JS wbudowany, podobnie jak <em>Image</em>, <em>Math</em> i inne. Obiekt ten posiada funkcję <em>getTime</em>, która zwraca aktualny czas w formie <a href="http://en.wikipedia.org/wiki/Unix_time">UNIXowej</a>. W ten sposób mamy unikalne <em>id</em>, które przyda nam się, by łatwo odwołać sie do konretnego boksu w tablicy zawierającej je wszystkie (<em>boxContainer</em>). O takiej:</p>
<pre><code>function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("&lt;div&gt;&lt;/div&gt;");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		<strong>boxContainer[id] = [];</strong></code></pre>
<p>W ten sposób mamy nowy element tablicy <em>boxContainer</em> o losowym kluczu w postaci identyfikatora elementu np. boxContainer[1223424325432] itd. Z tego elementu tablicy robimy kolejną tablicę (<span class="f">= []</span>), by umożliwić korzystanie z czegoś podobnego, na wzór tablicy wielowymiarowej: </p>
<pre><code>boxContainer[id]['cos']</code></pre>
<p>Paść może pytanie, po co nam taka tablica, skoro wszystkie <em>divy</em> będą należeć do <span class="f">#area</span>, co za tym idzie można je łatwo pobrać podobnym wyrażeniem:</p>
<pre><code>$("#area div");</code></pre>
<p>Sprawa rozbija się o&#8230; szybkość działania skryptu. Iteracja po tablicach jest znacznie szybsza niż po DOM. Jeszcze większą szybkość moglibyśmy uzyskać, stosując klucze liczbowe &#8211; czyli zamiast &#8220;cos&#8221; np. 2. Dla przejrzystości zachowamy jednak klucze będące stringiem. Za zastosowaniem tablic przemawia również łatwe dodawanie (i szybsze) właściwości danego boksu, a więc w tym wypadku jego kierunku poruszania.</p>
<p>Jeśli mowa o kierunku poruszania się, wylosujmy kierunek w pionie i poziomie. Zrobimy to losując liczbę od 1 do 2. Defaultowo boks poruszać będzie się w prawo i w dół. Jeśli za pierwszym razem wypadnie jedynka, element będzie poruszał się w lewo, jeśli to samo stanie się za drugim razem, zmieni się również kierunek w pionie.</p>
<pre><code>function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("&lt;div&gt;&lt;/div&gt;");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		boxContainer[id] = [];

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['x'] = 'right';

		if (arrow == 1)
		{
			boxContainer[id]['x'] = 'left';
		}

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['y'] = 'bottom';

		if (arrow == 1)
		{
			boxContainer[id]['y'] = 'top';
		}

</code></pre>
<p>W ten sposób, jeśli stworzymy element o id np. <em>222</em> (hipotetycznie), to tablica boxContainer wyglądać będzie tak:</p>
<pre><code>boxContainer = [222:
	[
		y: top || bottom; (kierunek poruszania się w pionie)
		x: left || right; (kierunek poruszania się w poziomie)
	]
]</code></pre>
<p>Wartości <em>x</em> i <em>y</em> zależą od wylosowanych liczb.</p>
<p>Zapiszmy jeszcze do tablicy z danym wygenerowanym elementem jego szerokość i wysokość. Po co pobierać ją za każdym razem, korzystając z DOM (<em>$(element).css("height")</em>), skoro można ten proces przyspieszyć:</p>
<pre><code>function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("&lt;div&gt;&lt;/div&gt;");

		<strong>var sizes = setBoxRandomSize(div);</strong>
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		boxContainer[id] = [];

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['x'] = 'right';

		if (arrow == 1)
		{
			boxContainer[id]['x'] = 'left';
		}

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['y'] = 'bottom';

		if (arrow == 1)
		{
			boxContainer[id]['y'] = 'top';
		}

		<strong>boxContainer[id]['w'] = sizes.width;
		boxContainer[id]['h'] = sizes.height;</strong></code></pre>
<p>Wyróżnione części kodu powinny pokazać, skąd wzięły się wartości <em>sizes</em>.</p>
<p>Mamy teraz tablicę asocjacyjną, której głównym kluczem jest identyfikator elementu. Potem do wyboru mamy 4 wartości - 2 kierunki poruszania się oraz szerokość i wysokość boksu.</p>
<p>Pora dodać teraz zdarzenie <em>onclick</em>, oznaczające usunięcie elementu z <span class="f">#area</span> oraz dodanie punktu.</p>
<pre><code>function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("&lt;div&gt;&lt;/div&gt;");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		boxContainer[id] = [];

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['x'] = 'right';

		if (arrow == 1)
		{
			boxContainer[id]['x'] = 'left';
		}

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['y'] = 'bottom';

		if (arrow == 1)
		{
			boxContainer[id]['y'] = 'top';
		}

		boxContainer[id]['w'] = sizes.width;
		boxContainer[id]['h'] = sizes.height;

		<strong>div.click(function(e)
		{
			if (!gameOver)
			{
				$(this).hide("slow", function()
				{
					$(this).remove();
					$("#result").text(parseInt($("#result").text())+1);
				createBoxes(Math.floor(boxAdditionalMaxCount*Math.random())+1);
				});
			}
		});</strong></code></pre>
<p>Po dodaniu zdarzenia, sprawdzamy w warunku <em>if</em>, czy gra działa, czy nie. Jeśli zmienna <em>gameOver</em> będzie miała wartość <em>false</em>, będzie to oznaczało, że rozgrywka jest w toku i należy przeprowadzić usunięcie elementu. Ten z kolei usuwamy, kiedy ukryjemy dany boks (funkcja <em>hide</em> i drugi argument, w postaci funkcji wykonywanej po ukończeniu chowania elementu). Całość zostaje usunięta z drzewa DOM dzięki funkcji <em>remove</em>. Potem dodajemy do wyniku jeden, pobierając go wcześniej i zamieniając, dla upewnienia się, na liczbę (<em>parseInt()</em>). Ostatnia czynność to dodanie do <span class="f">#area</span> od 1 do 3 nowych boksów (znowu urządzamy małe losowanie, korzystając z wiadomości poznanych wyżej).</p>
<p>Ostatnią rzeczą, którą musimy się zająć, jest oczywiście dodanie powstałego <em>diva</em> do kontenera, w którym będzie się poruszał:</p>
<pre><code>function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("&lt;div&gt;&lt;/div&gt;");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		boxContainer[id] = [];

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['x'] = 'right';

		if (arrow == 1)
		{
			boxContainer[id]['x'] = 'left';
		}

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['y'] = 'bottom';

		if (arrow == 1)
		{
			boxContainer[id]['y'] = 'top';
		}

		boxContainer[id]['w'] = sizes.width;
		boxContainer[id]['h'] = sizes.height;

		div.click(function(e)
		{
			if (!gameOver)
			{
				$(this).hide("slow", function()
				{
					$(this).remove();
					$("#result").text(parseInt($("#result").text())+1);
				createBoxes(Math.floor(boxAdditionalMaxCount*Math.random())+1);
				});
			}
		});

		<strong>$("#area").append(div);</strong></code></pre>
<p><a href="http://ferrante.pl/examples/jquery/jquery6/gra5.html">Voila</a>.</p>
<p>Pora wprawić w ruch powstałe boksy i uruchomić licznik. Zacznijmy od tego drugiego:</p>
<pre><code>function executeTimer()
{
	var time = parseInt($("#time").text());

	if (time-1 > -1)
	{
		$("#time").text(time-1);
		return false;
	}

	window.clearInterval(gameTimer);
	window.clearInterval(boxMovement);
	gameOver = true;

}</code></pre>
<p>W odpowiedzialnej za to funkcji <em>executeTimer</em>, którą, przypomnijmy, odpalamy cyklicznie, dzieje się wiele istotnych, ale równie łatwych rzeczy. Po pierwsze, pobieramy stąd:</p>
<pre><code>&lt;h3&gt;Pozostało:&lt;/h3&gt;&lt;h4&gt;&lt;span id="time"&gt;30&lt;/span&gt; sekund&lt;/h4&gt;</code></pre>
<p>ile zostało nam czasu na zabawę:</p>
<pre><code>var time = parseInt($("#time").text());</code></pre>
<p>Znów używamy <em>parseInt</em>, by uniknąć jakichkolwiek niespodzianek.</p>
<p>Potem sprawdzamy, czy odejmując jeden od pobranej liczby sekund nie uzyskamy czasem liczby ujemnej, co byłoby przecież nielogiczne (jak może nam zostać do końca rozgrywki -1 sekund?). Jeśli więc wszystko pójdzie w porządku, rzeczywiście odejmujemy jeden od ogólnej liczby sekund i aktualizujemy licznik, dzięki funkcji jQuery <em>text</em>. <em>return false;</em> w tym wypadku kończy działanie funkcji, która pominie pozostały kod. </p>
<p>Ten z kolei zostanie uruchomiony, kiedy przekroczymy czas gry, a więc do końca zostanie nam te <em>-1</em> sekund. Wtedy to usuwamy możliwość cyklicznego wykonywania ustawionych wcześniej funkcji oraz zmieniamy wartość zmiennej <em>gameOver</em> na <em>true</em>. Oznacza to po prostu koniec gry (wówczas zdarzenie <em>onclick</em>, które dodaliśmy wcześniej dla elementów nie uruchomi się, ze względu na wartość zmiennej <em>gameOver</em>).</p>
<p>Pozostało nam już tylko uruchomić poruszanie się boksów. Musimy dotrzeć do każdego z nich w tablicy <em>boxContainer</em> i sprawdzić jego położenie. Na jego podstawie ustalimy kierunek ruchu. </p>
<p>Dostać się do każdego elementu tablicy pomoże nam znów pętla <em>for</em>:</p>
<pre><code>function moveBoxes()
{
	for (k in boxContainer)
	{</code></pre>
<p>Użyta w ten sposób działa podobnie do PHPowej funkcji <em>foreach</em> - po prostu iteruje po każdym elemencie tablicy <em>boxContainer</em>, której kluczem jest zmienna <em>k</em>. Dostajemy wtedy każdy element tablicy w postaci:</p>
<pre><code>boxContainer[k];</code></pre>
<p>O ile dobrze przypominasz sobie dodawanie elementów do tej tablicy, zapewne wiesz, że pod <em>k</em> znajdują się po prostu identyfikatory <em>divów</em>. Wykorzystajmy to, by pobrać ich aktualną pozycje z drzewa DOM:</p>
<pre><code>function moveBoxes()
{
	for (k in boxContainer)
	{
		<strong>var top = parseInt($("#"+k).css("top"));
		var left = parseInt($("#"+k).css("left"));</strong></code></pre>
<p>Teraz, w zależności od kierunku poruszania się danego elementu, ustalimy zaktualizowane wartości stylów <em>left</em> i <em>top</em> dla <em>diva</em>:</p>
<pre><code>function moveBoxes()
{
	for (k in boxContainer)
	{
		var top = parseInt($("#"+k).css("top"));
		var left = parseInt($("#"+k).css("left"));

		<strong>if (boxContainer[k]['y'] == 'top')
		{
			var top = top-boxMovementVertical;
		}
		else
		{
			var top = top+boxMovementVertical;
		}
		if (boxContainer[k]['x'] == 'left')
		{
			var left = left-boxMovementHorizontal;
		}
		else
		{
			var left = left+boxMovementHorizontal;
		}</strong></code></pre>
<p>Jak widać, w zależności od kierunku, dodajemy bądź odejmujemy od tych wartości 10 pikseli (wartości zmiennych <em>boxMovementHorizontal</em> i <em>boxMovementVertical</em>). Biorąc pod uwagę, że funkcja <em>moveBoxes</em> jest uruchamiana co 200milisekund, da to nam złudzenie ruchu wszystkich boksów.</p>
<p>Oczywiście musimy sprawdzić, czy wartości <em>left</em> i <em>top</em> nie są za małe bądź za duże. Wtedy to boks wyjdzie poza obszar <span class="f">#area</span>. Sprawdzamy więc, kiedy dany klocek dotknie granic dozwolonego obszaru. Jeśli poruszający się w prawo natknie się na prawą krawędź, wtedy jego dalszy ruch w prawo nie ma sensu - zmieniamy mu kierunek na lewy. To samo ma się, jeśli chodzi o ruch pionowy.</p>
<pre><code>function moveBoxes()
{
	for (k in boxContainer)
	{
		var top = parseInt($("#"+k).css("top"));
		var left = parseInt($("#"+k).css("left"));

		if (boxContainer[k]['y'] == 'top')
		{
			var top = top-boxMovementVertical;
		}
		else
		{
			var top = top+boxMovementVertical;
		}
		if (boxContainer[k]['x'] == 'left')
		{
			var left = left-boxMovementHorizontal;
		}
		else
		{
			var left = left+boxMovementHorizontal;
		}

		<strong>if (left <= areaPosition.left)
		{
			boxContainer[k]['x'] = 'right';
			var left = areaPosition.left;
		}
		if (left+boxContainer[k]['w'] >= areaPosition.left+areaSize.w)
		{
			boxContainer[k]['x'] = 'left';
			var left = areaPosition.left+areaSize.w-boxContainer[k]['w'];
		}

		if (top < areaPosition.top)
		{
			boxContainer[k]['y'] = 'bottom';
			var top = 0;
		}
		if (top+boxContainer[k]['h'] >= areaSize.h+areaPosition.top)
		{
			boxContainer[k]['y'] = 'top';
			var top = areaPosition.top+areaSize.h-boxContainer[k]['h'];
		}</strong></code></pre>
<p>Pozostaje nam teraz zaktualizować dane wartości stylów każdego boksu:</p>
<pre><code>function moveBoxes()
{
	for (k in boxContainer)
	{
		var top = parseInt($("#"+k).css("top"));
		var left = parseInt($("#"+k).css("left"));

		if (boxContainer[k]['y'] == 'top')
		{
			var top = top-boxMovementVertical;
		}
		else
		{
			var top = top+boxMovementVertical;
		}
		if (boxContainer[k]['x'] == 'left')
		{
			var left = left-boxMovementHorizontal;
		}
		else
		{
			var left = left+boxMovementHorizontal;
		}

		if (left <= areaPosition.left)
		{
			boxContainer[k]['x'] = 'right';
			var left = areaPosition.left;
		}
		if (left+boxContainer[k]['w'] >= areaPosition.left+areaSize.w)
		{
			boxContainer[k]['x'] = 'left';
			var left = areaPosition.left+areaSize.w-boxContainer[k]['w'];
		}

		if (top < areaPosition.top)
		{
			boxContainer[k]['y'] = 'bottom';
			var top = 0;
		}
		if (top+boxContainer[k]['h'] >= areaSize.h+areaPosition.top)
		{
			boxContainer[k]['y'] = 'top';
			var top = areaPosition.top+areaSize.h-boxContainer[k]['h'];
		}

		<strong>$("#"+k).css({"left": left, "top": top})</strong>;</code></pre>
<p>W ten sposób <a href="http://ferrante.pl/examples/jquery/jquery6/gra.html">otrzymaliśmy dość prostą grę</a>. Można ją uzupełnić o inne efekty, o których tutaj nie wspomniałem. Można również pewne rzeczy bardziej zoptymalizować. Pierwsze należy z pewnością do Was. Z chęcią zobaczyłbym efekty przeróbek tego skryptu. Będą śmiałkowie?</p>
<p>Co do drugiego wątku, optymalizacja w grach, w JS przede wszystkim, to klucz do sukcesu. Wkrótce postaram się zaprezentować zgrabny artykuł o innych technikach optymalizacji JS, o których tutaj nie wspomniałem.</p>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2008/06/05/jquery-to-latwe-6-piszemy-prosta-gre/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>Przydatne triki w JavaScript #1</title>
		<link>http://ferrante.pl/2008/04/01/przydatne-triki-w-javascript-1/</link>
		<comments>http://ferrante.pl/2008/04/01/przydatne-triki-w-javascript-1/#comments</comments>
		<pubDate>Tue, 01 Apr 2008 19:36:49 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Vademecum]]></category>

		<guid isPermaLink="false">http://ferrante.pl/2008/04/01/przydatne-triki-w-javascript-1/</guid>
		<description><![CDATA[JavaScript w wersji 1.8 to kawałek bardzo fajnych i użytecznych funkcji oraz konstrukcji. Niestety, nim wszystkie przeglądarki zaczną obsługiwać tę wersję języka, minie spory kawałek czasu. Powszechnie używana wersja 1.5 na szczęście także potrafi pokazać pazurki.
(5).plus(1).minus(2) = 4
Jedną z zalet JS jest możliwość używania podobnych, dzięki łatwej implementacji tzw. chainingu, czyli łączenia łańcuchów kodu. Jak [...]]]></description>
			<content:encoded><![CDATA[<p>JavaScript w wersji 1.8 to kawałek bardzo fajnych i użytecznych funkcji oraz konstrukcji. Niestety, nim wszystkie przeglądarki zaczną obsługiwać tę wersję języka, minie spory kawałek czasu. Powszechnie używana wersja 1.5 na szczęście także potrafi pokazać pazurki.<span id="more-84"></span></p>
<h3>(5).plus(1).minus(2) = 4</h3>
<p>Jedną z zalet JS jest możliwość używania podobnych, dzięki łatwej implementacji tzw. <em>chainingu</em>, czyli łączenia łańcuchów kodu. Jak coś podobnego zrobić?</p>
<p>Zaczynamy od interpretacji działania takiej linijki kodu. Wszystko zaczyna się od liczby 5 zapisanej w nawiasie. Jest to dla nas sygnał, że liczba 5 musi być obiektem, ponieważ inaczej niemożliwe byłoby wykonywanie nań żadnych funkcji. Jakiego typu obiektem jest ta liczba możemy sprawdzić dzięki <span class="f">typeof</span>.</p>
<pre><code>alert(typeof 5);</code></pre>
<p>Powyższa linijka zwróci nam dumnie brzmiące słowo <em>number</em>. Nie trzeba dużej bystrości by stwierdzić, że do czynienia mamy z obiektem reprezentującym liczbę.</p>
<p>Próba wywołania na tym obiekcie funkcji <span class="f">plus()</span> bądź <span class="f">minus()</span> kończy się jednak niepowodzeniem &#8211; JS nie potrafi odnaleźć takich funkcji, które byłyby użyte w kontekście obiektu <em>number</em>. Ergo, nie posiada on takich metod/funkcji. JavaScript jest jednak na tyle elastyczny, że udostępnia stosowne narzędzia, które pomagają nam uzupełnić obiekt o jakie tylko zapragniemy funkcje. Jest to właściwość <span class="f">prototype</span>, używana tak:</p>
<pre><code>obiekt.prototype.nazwa_nowej_funkcji = function() {};</code></pre>
<p>Po tym zabiegu <em>obiekt</em> będzie posiadał w swoim asortymencie funkcję o nazwie <em>nazwa_nowej_funkcji</em>:</p>
<pre><code>obiekt.nazwa_nowej_funkcji();</code></pre>
<p>Pora teraz wykorzystać to w kontekście obiektu <em>number</em>. Zaimplementujmy więc funkcję <em>plus</em>:</p>
<pre><code>number.prototype.plus = function(i) { return parseInt(this+i); };</code></pre>
<p>Przetestujmy teraz co nieco:</p>
<pre><code>var liczba = (5).plus(5); alert(liczba);</code></pre>
<p>Nie działa&#8230; Przyczyna jest prosta &#8211; istnieje tylko obiekt <em>Number</em>, a nie <em>number</em>. Wszystko przez <em>typeof</em>, które <span class="f">zwraca nazwy typów obiektów pisane małymi literami</span>. Poprawmy więc, co trzeba:</p>
<pre><code>Number.prototype.plus = function(i) { return parseInt(this+i); };</code></pre>
<p>Teraz podany przykład działa w najlepsze.</p>
<p>Czas na dodanie funkcji <em>minus</em>:</p>
<pre><code>Number.prototype.minus = function(i) { return parseInt(this-i); };</code></pre>
<p>I przetestowanie przykładu z nagłówka:</p>
<pre><code>var liczba = (5).plus(1).minus(2); alert(liczba);</code></pre>
<p>Powinna pojawić się liczba 4.</p>
<p>Zastanówmy się teraz spokojnie jak to wszystko działa. Otóż najpierw JS szuka wewnątrz obiektu <em>Number</em> funkcji <em>plus</em>. Gdy się to stanie, funkcja <em>plus</em> wykonuje się, zwracając liczbę będącą równowartością sumy <em>this</em> i liczby podanej w argumencie funkcji (jeden). Jako że 5 to liczba, jak i obiekt, na którym wykonujemy funkcję <em>plus</em>, <em>this</em> odnosi się właśnie do jego wartości (5).  Funkcja <em>minus</em> wykonywana jest podobnie &#8211; tyle że w <em>this</em>, zamiast pięć, mamy teraz sześć, bo <em>minus()</em> odnosi się do obiektu, w kontekście którego jest wykonywana (to, co jest przed kropką oddzielającą funkcję <em>minus</em> od reszty, gwoli ścisłości).</p>
<h3>dodaj(2)(3)</h3>
<p>Coś takiego jest dość proste do zrobienia, wystarczy trochę pomyśleć. Przecież aby można było podstawić drugi nawias <span class="f">(3)</span> do <span class="f">dodaj(2)</span>, <em>dodaj(2)</em> musi być funkcją. Tylko funkcje można uruchamiać w takim zapisie: <em>nazwafunkcji(argument)</em>. <em>dodaj(2)</em> musi być więc funkcją, co znaczy nie więcej, jak to, że musi zwracać w swoim ciele &#8230; inną funkcję. Coś w ten deseń:</p>
<pre><code>function funkcja() { return function() {}; }</code></pre>
<p>Funkcja, zwracająca anonimową funkcję. Zastosujmy to teraz do naszego przykładu:</p>
<pre><code>function dodaj() { return function(){}; }</code></pre>
<p>No dobra, ale co dalej? Jak widać, funkcja <em>dodaj</em> musi przyjąć jako argument dowolną liczbę, a potem, podstawić ją do funkcji, którą zwraca, by tam dodać ją do argumentu, który przyjmuje z kolei zwracana, anonimowa funkcja.</p>
<pre><code>function dodaj(i) { return function(i2) { return parseInt(i+i2); }; }</code></pre>
<p><em>parseInt</em> sprawia, że niezależnie jakie wartości byśmy podstawili, JS postara się zawsze powstałą sumę przekonwertować na obiekt typu <em>Number</em>.</p>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2008/04/01/przydatne-triki-w-javascript-1/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Ruchome zakładki, mootools i problemy z onmouseout</title>
		<link>http://ferrante.pl/2008/03/29/ruchome-zakladki-mootools-i-problemy-z-onmouseout/</link>
		<comments>http://ferrante.pl/2008/03/29/ruchome-zakladki-mootools-i-problemy-z-onmouseout/#comments</comments>
		<pubDate>Sat, 29 Mar 2008 21:33:57 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Vademecum]]></category>

		<guid isPermaLink="false">http://ferrante.pl/2008/03/29/ruchome-zakladki-mootools-i-problemy-z-onmouseout/</guid>
		<description><![CDATA[
Ostatni projekt, którym się zajmuje, zawiera imitację flashowej reklamy z trzema rozwijanymi od dołu sloganami, chwalącymi zalety firmy. Implementacja tego rozwiązania, biorąc pod uwagę obecność efektów animacyjnych, nastręcza dużych kłopotów z dziwnym działaniem zdarzenia onmouseout. Postaram się więc przedstawić rozwiązanie problemu &#8211; tym razem oparte o mootools.
Dokument HTML
By rozpocząć naszą przygodę z omawianym tematem, przyjrzyjmy [...]]]></description>
			<content:encoded><![CDATA[<p class="p"><a href='http://ferrante.pl/2008/03/29/ruchome-zakladki-mootools-i-problemy-z-onmouseout/zakladki-onmouseout/' rel='attachment wp-att-82' title='zakladki-onmouseout'><img src='http://ferrante.pl/wp-content/uploads/2008/03/zakladki.jpg' alt='zakladki-onmouseout' /></a></p>
<p>Ostatni projekt, którym się zajmuje, zawiera imitację flashowej reklamy z trzema rozwijanymi od dołu sloganami, chwalącymi zalety firmy. Implementacja tego rozwiązania, biorąc pod uwagę obecność efektów animacyjnych, nastręcza dużych kłopotów z dziwnym działaniem zdarzenia onmouseout. Postaram się więc przedstawić rozwiązanie problemu &#8211; tym razem oparte o mootools.<span id="more-83"></span></p>
<h3>Dokument HTML</h3>
<p>By rozpocząć naszą przygodę z omawianym tematem, <a href="http://ferrante.pl/examples/js/ruchome_zakladki/szablon.html">przyjrzyjmy się</a> najpierw szablonowi HTML, na którym będziemy pracować.</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Ruchome zakładki&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;

	&lt;style type="text/css"&gt;
		* { margin: 0; padding: 0 }
		body { font: 12px normal, 'Georgia'; }
		div { width: 500px; margin: auto; }
		div ul { display: block; width: 400px; height: 200px; background: #BAE4F0; list-style-type: none; overflow: hidden; }
		div ul li { display: block; width: 120px; height: 100%; float: left; background: #A9D26A; margin-top: 150px; margin-left: 10px;  text-align: center; }
		div ul li h2 { margin-bottom: 30px; font-size: 20px; }
	&lt;/style&gt;
 &lt;/head&gt;

 &lt;body&gt;
	&lt;div&gt;
		&lt;ul&gt;
			&lt;li>&lt;h2>fast&lt;/h2>&lt;p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.&lt;/p>&lt;/li>
			&lt;li>&lt;h2>cool&lt;/h2>&lt;p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.&lt;/p>&lt;/li>
			&lt;li&gt;&lt;h2&gt;best&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.&lt;/p&gt;&lt;/li&gt;
		&lt;/ul&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Jak widać, każdy element listy nieuporządkowanej ma dość duży margines, który wypycha treść poza kontener. Zastosowanie <span class="f">overflow: hidden;</span> skutecznie ukrywa ją. </p>
<p>Ogólne założenie co do skryptu jest takie, by po najechaniu myszką na element listy zmienić <span class="f">margin-top</span> na <span class="f">0px</span>. Załóżmy przy tym, że nie stanie się to od razu poprzez ustawienie określonej wartości, np. <em>style.marginTop = 0;</em>. Aby całość prezentowała się schludnie, do rozwijania zakładki użyjemy efektów animacyjnych.</p>
<p>Zwijanie elementu, po opuszczeniu pola zakładki przez kursor myszki, będzie się odbywać analogicznie &#8211; ustawimy <em>margin-top</em> na domyślne 150px. Nie obędzie się bez ładnej animacji.</p>
<h3>onmouseout</h3>
<p>Przed przystąpieniem do działania, musimy omówić sobie istotę problemu, który został poruszony we wstępie. Otóż załóżmy, że mamy ustawione dwa zdarzenia (<span class="f">onmouseover</span> i <span class="f">onmouseout</span>) dla <em>div#foobar</em>:</p>
<pre><code>&lt;div id="foobar"&gt;
	&lt;p style="background: #cc0000; color: #fff; margin: 0;"&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nam ultrices, dolor pellentesque dignissim volutpat, ante felis aliquet elit, nec ornare neque dolor quis eros. Cras auctor velit et magna. Quisque vitae pede eget justo vehicula lacinia. Aenean mauris. Nullam vitae velit a nisl aliquet dictum.&lt;/p&gt;
	&lt;p style="background: #cc0000; color: #fff; margin: 0;"&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nam ultrices, dolor pellentesque dignissim volutpat, ante felis aliquet elit, nec ornare neque dolor quis eros. Cras auctor velit et magna. Quisque vitae pede eget justo vehicula lacinia. Aenean mauris. Nullam vitae velit a nisl aliquet dictum.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<p>Ku naszemu zdziwieniu, zdarzenie <em>onmouseout</em> jest uruchamiane nie tylko po opuszczeniu obszaru diva, ale także podczas poruszania kursorem w jego obrębie. Dzieje się tak dlatego, że JS, i nie jest to wina żadnej z przeglądarek, interpretuje <em>childNodes</em> jako oddzielne elementy. Potwierdza to poniższy, pełny przykład:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Ruchome zakładki&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;

	&lt;script type="text/javascript"&gt;
	window.onload = function()
	{
		var foobar = document.getElementById('foobar');

		foobar.onmouseover = function() { alert('over'); };
		foobar.onmouseout = function() { alert('out'); };
	}
	&lt;/script&gt;
 &lt;/head&gt;

 &lt;body&gt;
	&lt;div id="foobar"&gt;
		&lt;p style="background: #cc0000; color: #fff; margin: 0;"&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nam ultrices, dolor pellentesque dignissim volutpat, ante felis aliquet elit, nec ornare neque dolor quis eros. Cras auctor velit et magna. Quisque vitae pede eget justo vehicula lacinia. Aenean mauris. Nullam vitae velit a nisl aliquet dictum.&lt;/p&gt;
		&lt;p style="background: #cc0000; color: #fff; margin: 0;"&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nam ultrices, dolor pellentesque dignissim volutpat, ante felis aliquet elit, nec ornare neque dolor quis eros. Cras auctor velit et magna. Quisque vitae pede eget justo vehicula lacinia. Aenean mauris. Nullam vitae velit a nisl aliquet dictum.&lt;/p&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><a href="http://ferrante.pl/examples/js/ruchome_zakladki/mouseout_alert.html">Ilość uruchomień funkcji </a><em>alert</em> jest niepokojąca duża.</p>
<p>Zasadniczo istnieją dwa sposoby na wyeliminowanie problemu. Jedna opiera się o funkcję z IE &#8211; <span class="f">contains</span>, której implementację trzeba dodać do innych, nie obsługujących jej, przeglądarek. Drugą metodą jest żmudne sprawdzanie, z jakiego elementu został zwolniony kursor i na który najechał podczas wykonywania się zdarzenia <em>onmouseout</em>. Pierwszy zawiera elementy drugiego, więc omówmy go.</p>
<h3>element.contains()</h3>
<p>Funkcja ta powinna sprawdzać, czy element podany w argumencie jest dzieckiem elementu ze zmiennej <em>element</em>. Zanim dojdzie do jej kodowania, powiedzmy sobie parę ciekawych rzeczy.</p>
<p>Podczas wykonywania zdarzeń mamy do dyspozycji obiekt <span class="f">Event</span>, który możemy przekazać do funkcji wykonującej dane zdarzenie:</p>
<pre><code>element.onclick = function(e <-- <strong>o, tutaj</strong>) { };</code></pre>
<p>Dla IE oczywiście pod <em>e</em> nic nie będzie i trzeba ratować się poniższym:</p>
<pre><code>element.onclick = function(e) { if (!e) var e = window.event; };</code></pre>
<p>Obiekt ten zawiera cenne informacje, jak na przykład z jakiego elementu wraca kursor myszki podczas <em>onmouseover</em>:</p>
<pre><code>element.onmouseover = function(e) { alert(e.fromElement); };</code></pre>
<p>bądź na jaki właśnie najechał podczas <em>onmouseout</em>:</p>
<pre><code>element.onmouseout = function(e) { alert(e.toElement); };</code></pre>
<p>Niestety, <span class="f">Event.toElement</span> i <span class="f">Event.fromElement</span> nie są zaimplementowane w IE, gdzie trzeba użyć zamiennego dla obydwu <span class="f">Event.relatedTarget</span>:</p>
<p><cpre><code>var element = e.relatedTarget || e.toElement;</code></pre>
<p>Podobna linijka załatwia sprawę - jeśli przeglądarka nie obsługuje <em>relatedTarget</em> (Opera, Fx i inne), to do zmiennej element przypisana zostanie własność <em>toElement</em>.</p>
<p>Warto też pamiętać, że każdy element drzewa DOM jest w JS reprezentowany przez klasę/obiekt HTMLElement. Klasami dziedziczącymi są np. HTMLParagraphElement, HTMLSpanElement i tak dalej. Jeśli więc zastosujemy coś takiego:</p>
<pre><code>element.onclick = function(e) { alert(this); };</code></pre>
<p>Z reguły, w okienku powinny pojawić się nazwy obiektów, jak powyżej.</p>
<p>Otóż tę wiedzę można wykorzystać do napisania funkcji <span class="f">contains</span>. Będzie ona wywoływana np. tak:</p>
<pre><code>document.getElementById('parent').contains(document.getElementById('child'));</code></pre>
<p>Zwróci prawdę, kiedy element o identyfikatorze <em>parent</em> zawierał będzie element o identyfikatorze <em>child</em>. Jak już wiemy, funkcja ta działa w IE. Trzeba więc uzupełnić o nią obiekt HTMLElement w każdej innej przeglądarce. Obiekty uzupełniać możemy poprzez funkcję <span class="f">prototype</span> użytą w kontekście obiektu:</p>
<pre><code>obiekt.prototype.foobar = function () { alert('to jest nowa funkcja w obiekcie "obiekt"');</code></pre>
<p>Od tej pory możliwe jest używanie czegoś na wzór:</p>
<pre><code>obiekt.foobar();</code></pre>
<p>Uzupełnijmy więc obiekt HTMLElement o funkcję <em>contains</em>:</p>
<pre><code>HTMLElement.prototype.contains = function(node)
{
	if (node == null)
	{
		return false;
	}
	else if (node == this)
	{
		return true;
	}

	return this.contains(node.parentNode);
}</code></pre>
<p>I tak, jeśli wskazany element DOM jest pusty - funkcja zwraca nieprawdę, jeśli obiekt typu HTMLElement zawiera <em>node</em>, zwraca prawdę. Jeśli niespełniony jest żaden z tych warunków, funkcja rekurencyjnie pobiera kolejnych rodziców <em>node</em>. Jeśli odnajdzie szukanego rodzica, zwróci prawdę.</p>
<p>Przyjmijmy taki przykład:</p>
<pre><code>&lt;div id="div"&gt;&lt;span&gt;&lt;small id="small"&gt;&lt;/small&gt;&lt;/span&gt;&lt;/div&gt;</code></pre>
<p>Pobieramy odnośnik do diva i szukamy w nim elementu o <em>#small</em>:</p>
<pre><code>document.getElementById('div').contains(document.getElementById('small'));</code></pre>
<p>Uruchamiana jest więc funkcja <em>contains</em>, która szuka elementu <em>#small</em>. Analizując krok po kroku:</p>
<ul>
<li><em>document.getElementById('small')</em> nie jest puste, więc przechodzimy do następnego warunku.</li>
<li><em>document.getElementById('small')</em> nie równa się <em>document.getElementById('div')</em>, wiadomo. Uruchamiana jest więc funkcja <em>contains</em>, która sprawdza, czy rodzic <em>document.getElementById('small')</em> zawiera się w <em>document.getElementById('div')</em>.</li>
<li>I od nowa - rodzic, czyli element <em>span</em> nie jest pusty, bo istnieje w dokumencie. Nie równa się nadal <em>document.getElementById('div')</em>. Więc sprawdźmy, czy rodzic <em>spana</em>, czyli <em>div</em>, zawiera się w <em>div</em>. Ekhm, masło maślane, ale to prawda, bo spełni się warunek:
<pre><code>else if (node == this)
{
	return true;
}</code></pre>
</li>
</ul>
<p>Cała funkcja opiera się na tautologii, mówiącej, że jeśli rodzic elementu należy do jakiegoś elementu, to również ten element do niego należy. Zajmijmy się teraz naszymi przykładami.</p>
<h3>Moo Tools</h3>
<p>Do obsłużenia przykładowego skryptu musimy posiadać całe komponenty Core, Native i Class oraz część Effects: Fx.Base, Fx.CSS i Fx.Style. </p>
<p>Skrypt powinien rozpocząć się po załadowaniu DOM, więc:</p>
<pre><code>window.addEvent('domready', function()
{
});</code></pre>
<p>Potem pobieramy elementy <em>li</em> i nadajemy im stosowne zdarzenia.</p>
<pre><code>window.addEvent('domready', function()
{
	$$('div ul li').each(function(element)
	{
		element.addEvent('mouseover', function(e) {});
		element.addEvent('mouseout', function(e) {});
	});

});</code></pre>
<p>Tak działają selektory w mootools ;-).</p>
<p>Oprócz zdarzeń, musimy stworzyć też nowe efekty animacyjne dla każdego elementu, opierając się na zmianie <em>margin-top</em>:</p>
<pre><code>window.addEvent('domready', function()
{
	$$('div ul li').each(function(element)
	{
		var efekt = new Fx.Style(element, 'margin-top', {duration: 1000 });
		var efekt2 = new Fx.Style(element, 'margin-top', {duration: 1000 });

		element.addEvent('mouseover', function() { efekt.start(0); });
		element.addEvent('mouseout', function() { efekt2.start(150); });
	});

});</code></pre>
<p>Animacja powinna trwać 1000ms (duration: 1000).</p>
<p>Analizując powyższy przykład stwierdzamy, że po najechaniu myszką na zakładkę, uruchamia się animacja, która ustawia na końcu wartość <em>margin-top</em> na 0px. Po zwolnieniu kursora, animacja dąży do wartości 150px. <a href="http://ferrante.pl/examples/js/ruchome_zakladki/moo_first.html">Dzieje się jednak coś dziwnego</a>.</p>
<p>Zapewne to przez wielokrotne uruchamianie <em>onmouseout</em>. Wykorzystajmy więc funkcję <em>contains</em>, która sprawdzi, czy element, do którego zawitał kursor myszy, to tak naprawdę element należący do zakładki, czy nie. Jeśli nie, pozwolimy włączyć animację, jeśli tak, zapomnijmy o tym.</p>
<pre><code>window.addEvent('domready', function()
{
	$$('div ul li').each(function(element)
	{
		var efekt = new Fx.Style(element, 'margin-top', {duration: 1000 });
		var efekt2 = new Fx.Style(element, 'margin-top', {duration: 1000 });

		element.addEvent('mouseover', function() { efekt.start(0); });
		element.addEvent('mouseout', function(e)
		{
			if (!element.contains(e.relatedTarget || e.toElement))
			{
				efekt2.start(150);
			}

		});
	});

});</code></pre>
<p><a href="http://ferrante.pl/examples/js/ruchome_zakladki/moo_second.html">Jest ładnie</a>, ale mogłoby być lepiej. Kiedy trwa animacja rozwijania jednej zakładki, a my wskazaliśmy na inną, dobrze byłoby zatrzymać jej pokazywanie, by rozwijanie zakładki nie kolidowało z jej zwijaniem.</p>
<pre><code>window.addEvent('domready', function()
{
	$$('div ul li').each(function(element)
	{
		var efekt = new Fx.Style(element, 'margin-top', {duration: 1000 });
		var efekt2 = new Fx.Style(element, 'margin-top', {duration: 1000 });

		element.addEvent('mouseover', function() { efekt2.stop(); efekt.start(0); });
		element.addEvent('mouseout', function(e)
		{
			if (!element.contains(e.relatedTarget || e.toElement))
			{
				efekt.stop();
				efekt2.start(150);
			}

		});
	});

});</code></pre>
<p><a href="http://ferrante.pl/examples/js/ruchome_zakladki/moo_third.html">Jest świetnie</a>!</p>
<p>Używająć mootools możemy jeszcze bardziej uprościć sprawę - podpinając zamiast <em>mouseover</em> - zdarzenie <em>mousenter</em> oraz zamiast <em>mouseout</em> zdarzenie <em>mouseleave</em>. Nie musimy wtedy dbać o funkcję <em>contains</em>, bo zdarzenia te obsługują właśnie podobne do omawianych sytuacje. Nie zawsze jednak mamy do dyspozycji frameworki, więc dobrze znać rozwiązanie w czystym JS.</p>
<pre><code>element.addEvent('mouseenter', function() { efekt2.stop(); efekt.start(0); });
element.addEvent('mouseleave', function(e)
{
	efekt.stop();
	efekt2.start(150);

});</code></pre>
<p>Niezależnie od metody, popatrzmy na końcowy efekt:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Ruchome zakładki&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
 &lt;script type="text/javascript" src="mootools-release-1.11.js"&gt;&lt;/script&gt;
 	&lt;script type="text/javascript"&gt;

	window.addEvent('domready', function()
	{
		$$('div ul li').setStyle('margin-top', 150);
		$$('div ul li').each(function(element)
		{
			var efekt = new Fx.Style(element, 'margin-top', {duration: 1000 });
			var efekt2 = new Fx.Style(element, 'margin-top', {duration: 1000 });

			element.addEvent('mouseenter', function() { efekt2.stop(); efekt.start(0); });
			element.addEvent('mouseleave', function(e)
			{
					efekt.stop();
					efekt2.start(150);

			});
		});

	});
	&lt;/script&gt;

	&lt;style type="text/css"&gt;
		* { margin: 0; padding: 0 }
		body { font: 12px normal, 'Georgia'; }
		div { width: 500px; margin: auto; }
		div ul { display: block; width: 400px; height: 200px; background: #BAE4F0; list-style-type: none; overflow: hidden; }
		div ul li { display: block; width: 120px; height: 100%; float: left; background: #A9D26A; margin-top: 150px; margin-left: 10px;  text-align: center; }
		div ul li h2 { margin-bottom: 30px; font-size: 20px; }
	&lt;/style&gt;
 &lt;/head&gt;

 &lt;body&gt;
	&lt;div&gt;
		&lt;ul&gt;
			&lt;li&gt;&lt;h2&gt;fast&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.&lt;/p&gt;&lt;/li&gt;
			&lt;li&gt;&lt;h2&gt;cool&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.&lt;/p&gt;&lt;/li&gt;
			&lt;li&gt;&lt;h2&gt;best&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.&lt;/p&gt;&lt;/li&gt;
		&lt;/ul&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2008/03/29/ruchome-zakladki-mootools-i-problemy-z-onmouseout/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>jQuery &#8211; to łatwe! #5: Zakładki</title>
		<link>http://ferrante.pl/2008/03/24/jquery-to-latwe-5-zakladki/</link>
		<comments>http://ferrante.pl/2008/03/24/jquery-to-latwe-5-zakladki/#comments</comments>
		<pubDate>Mon, 24 Mar 2008 15:31:52 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Vademecum]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://ferrante.pl/2008/03/24/jquery-to-latwe-5-zakladki/</guid>
		<description><![CDATA[
Wiele portali zawiera sekcje, które pokazywane są za pomocą zakładek. Tego typu rozwiązania widziałeś pewnie nie raz. Sprawdza się to w miejscach, gdzie istnieje potrzeba zamieszczenia wielu treści, choć nie tylko.
Jest kilka sposobów na zrobienie dobrych zakładek, w tym wyróżnić można dwie grupy rozwiązań: oparte na ładowaniu treści do boxów poprzez AJAX lub ładowaniu wszystkich [...]]]></description>
			<content:encoded><![CDATA[<p class="p"><a href="http://ferrante.pl/2008/03/24/jquery-to-latwe-5-zakladki/" title="Zakładki w jQuery"><img src="http://ferrante.pl/wp-content/uploads/2008/03/zakladki-jquery.jpg" alt="Zakładki w jQuery" width="400" height="225" /></a></p>
<p>Wiele portali zawiera sekcje, które pokazywane są za pomocą zakładek. Tego typu rozwiązania widziałeś pewnie nie raz. Sprawdza się to w miejscach, gdzie istnieje potrzeba zamieszczenia wielu treści, choć nie tylko.<span id="more-75"></span></p>
<p>Jest kilka sposobów na zrobienie dobrych zakładek, w tym wyróżnić można dwie grupy rozwiązań: oparte na ładowaniu treści do boxów poprzez AJAX lub ładowaniu wszystkich boxów podczas generowania strony i odpowiednim modyfikowaniu ich <span class="f">display&#8217;em</span> w zależności, co chcemy oglądać. Omówmy każdą z opcji, poczynając od statycznego serwowania treści.
<div class="i"><strong>Uwaga!</strong> W poprzednich przykładach używaliśmy jQuery 1.1! Co prawda najnowsza wersja frameworka nie różni się od niej znacznie, jednak gdzieniegdzie przepisano kod, usunięto parę niepotrzebnych fragmentów, czy też dodano nowe funkcje, co samo w sobie stanowi powód do ściągnięcia nowej wersji.</div>
<p> Użyjemy oczywiście biblioteki jQuery w wersji <span class="f">1.2.3</span>.</p>
<p>We wszystkich przykładach będziemy modyfikować poniższy szablon. Warto zwrócić uwagę na strukturę dokumentu &#8211; zakładki realizujemy dzięki liście nieuporządkowanej, w poszczególnych elementach umieszczając linki. Dzięki temu, w razie wyłączonego JavaScriptu, użytkownik przejdzie do strony z zawartością (umownie) zakładki. Tak zwane accessibility i nieinwazyjny kod JS.</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="en"&gt;
&lt;head&gt;
	&lt;title&gt;&lt;/title&gt;
	&lt;meta http-equiv="content-type" content="text/html; charset=utf-8" /&gt;
	&lt;!-- dodanie jQuery do dokumentu --&gt;

	&lt;script type="text/javascript" src="jQuery.js"&gt;&lt;/script&gt;

	&lt;!-- Nasz kod Javascript: --&gt;

	&lt;style type="text/css"&gt;
		* { margin: 0; padding: 0; }
		body { font: normal 12px Tahoma; color: #000; }
		#container { width: 456px; margin: auto; }
		ul
		{
			width: 100%;
			border-bottom: 5px solid #376b8a;
			margin-top: 10px;
			overflow: hidden;
		}
		ul li
		{
			background: url('gfx/butt2.jpg') no-repeat;
			width: 76px;
			height: 22px;
			float: left;
			display: block;
			padding-top: 5px;
			text-align: center;
		}
		ul li a { text-decoration: none; color: #000; }
		ul li.active { background: url('gfx/butt1.jpg') no-repeat; 	color: #fff; }
		ul li.active a { color: #fff; }
		#bar1, #bar2, #bar3 { color: #fff; padding: 10px; background: #396B8C;  display: none;}
		#bar1 { display: block; }
	&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
	&lt;div id="container"&gt;
		&lt;ul id="bookmarks"&gt;&lt;li class="active"&gt;&lt;a href="#"&gt;Bar #1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#"&gt;Bar #2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#"&gt;Bar #3&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
		&lt;div id="content"&gt;
			&lt;div id="bar1"&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
			&lt;div id="bar2"&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
			&lt;div id="bar3"&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3>Sposób pierwszy</h3>
<p>Generalnie rzecz biorąc, całość jest oparta na zwykłej analogii. Otóż &#8211; jeśli klikniemy w pierwszą zakładkę, skrypt sprawdzi, która to z kolei zakładka (czyli który link <span class="f">&lt;a&gt;</span>) w znaczniku <span class="f">&lt;ul&gt;</span>. Kiedy będziemy dysponowali tą liczbą, spróbujemy dostać się do <span class="f">&lt;div&gt;</span>, który będzie tym samym z kolei elementem wewnątrz <span class="f">#content</span>. W skrócie &#8211; klikamy trzecią zakładkę, pokazuje się trzeci <em>div</em>. Do dzieła!</p>
<p>Zaczynamy jak zwykle od wychwycenia, kiedy drzewo DOM będzie załadowane, tym samym będąc przygotowane na zastrzyk naszego kodu JS:</p>
<pre><code>$().ready(function()
{</code></pre>
<p>Następnie, czas na dodanie zdarzenia <span class="f">onclick</span> dla każdego linku wewnątrz znacznika <em>li</em>:</p>
<pre><code>$().ready(function()
{
	$("ul#bookmarks li a").click(function()
	{</code></pre>
<p>Aby uznać zakładkę za aktywną, musi ona posiadać klasę <span class="f">active</span>, co wynika z kodu CSS na potrzeby przykładu. Wcześniej jednak, trzeba usunąć tę klasę z wszystkich innych elementów <em>&lt;li&gt;</em>, aby uniknąć dwóch aktywnych zakładek jednocześnie.</p>
<pre><code>$().ready(function()
{
	$("ul#bookmarks li a").click(function()
	{
		$("ul#bookmarks li.active").removeClass("active");
		$(this).parent().addClass("active");</code></pre>
<p>Przy pomocy odpowiedniego selektora dostaliśmy się do wszystkich elementów listy z klasą <em>active</em>, następnie, dzięki <span class="f">removeClass</span> usunęliśmy ją. Potem, odnosząc się do rodzica klikniętego elementu <em>&lt;a&gt;</em>, dodaliśmy doń klasę <em>active</em>. Innymi słowy, klikając w link, pobieramy element <em>&lt;li&gt;</em>, do którego należy odnośnik, aplikując stosowną klasę.</p>
<p>Następnie pora na pobranie do zmiennej <span class="f">element_index</span> liczby, oznaczającej, którym z kolei wewnątrz <em>#bookmarks</em> jest kliknięty link.</p>
<pre><code>$().ready(function()
{
	$("ul#bookmarks li a").click(function()
	{
		$("ul#bookmarks .active").removeClass("active");
		$(this).parent().addClass("active");

		var element_index = $("#bookmarks a").index(this);</code></pre>
<p>Sprawę załatwiła funkcja <span class="f">index()</span>. Otóż pobieramy tutaj wszystkie linki mieszczące się w <em>#bookmarks</em>. Następnie, dzięki funkcji <em>index</em> sprawdzamy, którym z kolei wśród pobranych linków w drzewie DOM jest ten, który kliknęliśmy (<em>this</em>). Tak więc funkcja ta działa w stosunku do pobranych elementów i sprawdza, którym wśród nich jest element podany jako jej argument. Warto zaznaczyć, że kolejność liczona jest naturalnie od zera. Pierwszy element będzie więc nim oznaczony.</p>
<p>Zbliżamy się do końca skryptu. Czas teraz na pobranie widocznych <em>divów</em> wśród <em>#content</em>, ukrycie ich i pokazanie właściwego, o indeksie takim, jaki przechowujemy w zmiennej <em>element_index</em>. Odnieść się do elementu o stosownym indeksie pozwala funkcja <span class="f">eq(numer indeksu)</span>. </p>
<pre><code>$().ready(function()
{
	$("ul#bookmarks li a").click(function()
	{
		$("ul#bookmarks .active").removeClass("active");
		$(this).parent().addClass("active");

		var element_index = $("#bookmarks a").index(this);

		$("#content div:visible").hide();
		$("#content div").eq(element_index).show();

		return false;
	});
});</code></pre>
<p>Na końcu przydaje się <span class="f">return false;</span>, by zapobiec przechodzeniu do strony podanej w klikniętym linku. Całość <a href="http://ferrante.pl/examples/jquery/jquery5/example1.html">prezentuje się</a> znakomicie:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="en"&gt;
&lt;head&gt;
	&lt;title&gt;&lt;/title&gt;
	&lt;meta http-equiv="content-type" content="text/html; charset=utf-8" /&gt;
	&lt;!-- dodanie jQuery do dokumentu --&gt;

	&lt;script type="text/javascript" src="js/jQuery.js"&gt;&lt;/script&gt;

	&lt;!-- Nasz kod Javascript: --&gt;
	&lt;script type="text/javascript"&gt;

		$().ready(function()
		{
			$("ul#bookmarks li a").click(function()
			{
				$("ul#bookmarks .active").removeClass("active");
				$(this).parent().addClass("active");

				var element_index = $("#bookmarks a").index(this);

				$("#content div:visible").hide();
				$("#content div").eq(element_index).show();

				return false;
			});
		});
	&lt;/script&gt;

	&lt;style type="text/css"&gt;
		* { margin: 0; padding: 0; }
		body { font: normal 12px Tahoma; color: #000; }
		#container { width: 228px; margin: auto; }
		ul
		{
			width: 100%;
			border-bottom: 5px solid #376b8a;
			margin-top: 10px;
			overflow: hidden;
		}
		ul li
		{
			background: url('gfx/butt2.jpg') no-repeat;
			width: 76px;
			height: 22px;
			float: left;
			display: block;
			padding-top: 5px;
			text-align: center;
		}
		ul li a { text-decoration: none; color: #000; }
		ul li.active { background: url('gfx/butt1.jpg') no-repeat; 	color: #fff; }
		ul li.active a { color: #fff; }
		#bar1, #bar2, #bar3 { color: #fff; padding: 10px; background: #396B8C;  display: none;}
		#bar1 { display: block; }
	&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
	&lt;div id="container"&gt;
		&lt;ul id="bookmarks"&gt;&lt;li class="active"&gt;&lt;a href="#"&gt;Bar #1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#"&gt;Bar #2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#"&gt;Bar #3&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
		&lt;div id="content"&gt;
			&lt;div id="bar1"&gt;&lt;p&gt;aLorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
			&lt;div id="bar2"&gt;&lt;p&gt;bLorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
			&lt;div id="bar3"&gt;&lt;p&gt;cLorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3>Sposób drugi</h3>
<p>Kolejnym sposobem na zakładki jest metoda bazująca na klasach elementów <em>&lt;a&gt;</em> i identyfikatorach boxów z treścią. Tutaj z kolei klasa zakładki odpowiada identyfikatorowi <em>diva</em>. Klikając zakładkę <span class="f">class=&#8221;box1</span> pokazujemy box o <span class="f">id=&#8221;box1&#8243;</span>. Zaktualizujmy więc nasz szablon na potrzeby przykładu:</p>
<pre><code>&lt;div id="container"&gt;
	&lt;ul id="bookmarks"&gt;&lt;li class="active"&gt;&lt;a href="#" class="bar1"&gt;Bar #1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#" class="bar2"&gt;Bar #2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#" class="bar3"&gt;Bar #3&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
	&lt;div id="content"&gt;
		&lt;div id="bar1"&gt;&lt;p&gt;aLorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
		&lt;div id="bar2"&gt;&lt;p&gt;bLorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
		&lt;div id="bar3"&gt;&lt;p&gt;cLorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
	&lt;/div&gt;
&lt;/div&gt;</code></pre>
<p>Pora na jQuery, poczynając od tego, co mamy:</p>
<pre><code>$().ready(function()
{
	$("ul#bookmarks li a").click(function()
	{
		$("ul#bookmarks .active").removeClass("active");
		$(this).parent().addClass("active");</code></pre>
<p>Pobierzmy teraz klasę klikniętego linku:</p>
<pre><code>$().ready(function()
{
	$("ul#bookmarks li a").click(function()
	{
		$("ul#bookmarks .active").removeClass("active");
		$(this).parent().addClass("active");

		var classname = $(this).attr("class");</code></pre>
<p>Nie pozostaje nam nic innego, jak podać otrzymaną klasę do selektora, który znajdzie nam odpowiedniego <em>diva</em>, o odpowiednim <em>#id</em>.</p>
<pre><code>$().ready(function()
{
	$("ul#bookmarks li a").click(function()
	{
		$("ul#bookmarks .active").removeClass("active");
		$(this).parent().addClass("active");

		var classname = $(this).attr("class");

		$("#content div:visible").hide();
		$("#content div#"+classname).show();

		return false;
	});
});</code></pre>
<p>Oczywiście chodzi o tę linijkę:</p>
<pre><code>$("#content div#"+classname).show("");</code></pre>
<p><a href="http://ferrante.pl/examples/jquery/jquery5/example2.html">Wszystko gra</a>.</p>
<p>Jeśli komuś pasuje rozwiązanie, <a href="http://ferrante.pl/examples/jquery/jquery5/example3.html">wykorzystujące moment najechania myszką</a> na zakładkę &#8211; nic łatwiejszego, podstawiamy zamiast <em>click</em>, zdarzenie <em>mouseover</em>:</p>
<pre><code>$().ready(function()
{
	$("ul#bookmarks li a").mouseover(function()
	{
		$("ul#bookmarks .active").removeClass("active");
		$(this).parent().addClass("active");

		var classname = $(this).attr("class");

		$("#content div:visible").hide();
		$("#content div#"+classname).show();

	});
});</code></pre>
<h3>Sposób trzeci &#8211; AJAX</h3>
<div class="i"><strong>AJAX w skrócie</strong><br />
AJAX to pewien obiekt, a upraszczając bardziej &#8211; funkcja, wbudowana w JavaScript przeglądarki, która pozwala w naszych skryptach pobrać zawartość z danego adresu (a więc strone HTML, dokument tekstowy, plik XML) nie odświeżając przy tym strony, na której korzystamy ze skryptów. AJAX działa także w drugą stronę, umożliwiając wysyłanie danych do jakieś strony (używając metody POST).</div>
<p>Ten sposób opiera się natomiast na wykorzystaniu AJAXa. Ma on swoje wady i zalety &#8211; po kliknięciu zakładki, JavaScript pobiera dla niej za każdym razem stosowną treść, co daje jedno wywołanie strony na kliknięcie. Moim zdaniem, jeśli dane nie dezaktualizują się często, lepiej ładować wszystkie boxy przy zwykłym generowaniu się strony po stronie serwera. Zalety to przede wszystkim jeden box w dokumencie HTML, a nie jak poprzednio &#8211; trzy.</p>
<p>Przyjrzyjmy się więc dokumentowi HTML dla sposobu z AJAXem:</p>
<pre><code>&lt;div id="container"&gt;
	&lt;ul id="bookmarks"&gt;&lt;li class="active"&gt;&lt;a href="#" class="bar1"&gt;Bar #1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#" class="bar2"&gt;Bar #2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#" class="bar3"&gt;Bar #3&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
	&lt;div id="content"&gt;
		&lt;div id="bar1"&gt;&lt;p&gt;aLorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
		&lt;/div&gt;
&lt;/div&gt;></code></pre>
<p>Jak już wiemy, AJAX potrafi załadować tekst lub HTML z jednej strony do drugiej. Aby więc coś znalazło się w naszych boxach, stwórzmy dokumenty HTML, z których pobierzemy stosowną treść: <span class="f"><a href="http://ferrante.pl/examples/jquery/jquery5/bar1.html">bar1.html</a></span>, <span class="f"><a href="http://ferrante.pl/examples/jquery/jquery5/bar2.html">bar2.html</a></span> i <span class="f"><a href="http://ferrante.pl/examples/jquery/jquery5/bar3.html">bar3.html</a></span>. Równie dobrze, jeśli generowalibyśmy ją dynamicznie, można by pobierać dane z adresu strony w PHP, Ruby etc. Na przykład: <span class="f">index.php?action=ajax&#038;what=bar1</span></span> czy też <span class="f">/ajax/get/bar1</span>. To, z jakiej strony będziemy czerpać kontent, to wyłącznie nasza sprawa, zależna od naszych potrzeb.</p>
<p>I tak, np. <em>bar1.html</em> wygląda następująco:</p>
<pre><code>&lt;p&gt;aLorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;</code></pre>
<p>Pora teraz na skrypty. jQuery dysponuje świetną (szczególnie od wersji 1.2) funkcją, dzięki której możemy załadować dany dokument HTML, wybrać przy okazji te elementy, które nas interesują, dzięki użyciu selektorów, by na końcu dodać otrzymaną treść do jakiegokolwiek elementu DOM. Mowa o funkcji <span class="f">load()</span>. Użyjemy jej!</p>
<p>Nim do tego dojdzie, wyjaśnijmy, jak będzie działał skrypt &#8211; na początku, co już zresztą zrobiliśmy, wychwycimy kliknięcie w zakładkę, uczynimy ją aktywną i tak dalej. Potem pobierzemy, dzięki funkcji <em>load</em>, stronę HTML o takiej nazwie,  jaką nosi atrybut <em>class</em> zakładki. Następnie, uzupełnimy nasz jedyny box uzyskaną treścią. Jeśli więc klikniemy zakładkę o <span class="f">class=&#8221;bar2&#8243;</span>, JS załaduje plik <em>bar2.html</em>. To prawie tak, jakbyśmy w pasek adresu przeglądarki wpisali <em>domena.pl/bar2.html</em>, skopiowali treść, włączyli edytor HTML i wkleili wszystko do <em>diva</em>.</p>
<pre><code>$().ready(function()
{
	$("ul#bookmarks li a").click(function()
	{
		$("ul#bookmarks .active").removeClass("active");
		$(this).parent().addClass("active");

		var classname = $(this).attr("class");

		$("#bar1").load(classname+".html p");

		return false;
	});
});</code></pre>
<p>W zasadzie sprawa zaczęła się i skończyła na jednej linijce: </p>
<pre><code>$("#bar1").load(classname+".html p");</code></pre>
<p>Reszta kodu pozostaje taka sama. Do elementu <em>#bar1</em> ładowana jest treść pliku HTML o nazwie klasy linku, który kliknęliśmy (zmienna <em>classname</em>). Równie dobrze moglibyśmy tutaj załadować całą stronę Wirtualnej Polski:</p>
<pre><code>$("#bar1").load("http://wp.pl");</code></pre>
<p>Całość <a href="http://ferrante.pl/examples/jquery/jquery5/example4.html">prezentuje się tak</a>:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="en"&gt;
&lt;head&gt;
	&lt;title&gt;&lt;/title&gt;
	&lt;meta http-equiv="content-type" content="text/html; charset=utf-8" /&gt;
	&lt;!-- dodanie jQuery do dokumentu --&gt;

	&lt;script type="text/javascript" src="js/jQuery.js"&gt;&lt;/script&gt;

	&lt;!-- Nasz kod Javascript: --&gt;
	&lt;script type="text/javascript"&gt;

		$().ready(function()
		{
			$("ul#bookmarks li a").click(function()
			{
				$("ul#bookmarks .active").removeClass("active");
				$(this).parent().addClass("active");

				var classname = $(this).attr("class");

				$("#bar1").load(classname+".html p");

				return false;
			});
		});
	&lt;/script&gt;

	&lt;style type="text/css"&gt;
		* { margin: 0; padding: 0; }
		body { font: normal 12px Tahoma; color: #000; }
		#container { width: 228px; margin: auto; }
		ul
		{
			width: 100%;
			border-bottom: 5px solid #376b8a;
			margin-top: 10px;
			overflow: hidden;
		}
		ul li
		{
			background: url('gfx/butt2.jpg') no-repeat;
			width: 76px;
			height: 22px;
			float: left;
			display: block;
			padding-top: 5px;
			text-align: center;
		}
		ul li a { text-decoration: none; color: #000; }
		ul li.active { background: url('gfx/butt1.jpg') no-repeat; 	color: #fff; }
		ul li.active a { color: #fff; }
		#bar1, #bar2, #bar3 { color: #fff; padding: 10px; background: #396B8C;  display: none;}
		#bar1 { display: block; }
	&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
	&lt;div id="container"&gt;
		&lt;ul id="bookmarks"&gt;&lt;li class="active"&gt;&lt;a href="#" class="bar1"&gt;Bar #1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#" class="bar2"&gt;Bar #2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#" class="bar3"&gt;Bar #3&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
		&lt;div id="content"&gt;
			&lt;div id="bar1"&gt;&lt;p&gt;aLorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec elementum. Nunc lacus. Donec porta. Suspendisse tortor est, hendrerit eu, ullamcorper in, laoreet eu, mauris. Vestibulum porttitor metus vel enim. Nunc dignissim libero a ligula.&lt;/p&gt;&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Oczywiście proces ładowania zakładek można wzbogacić stosownym komunikatem, czy efektami animacyjnymi &#8211; o tym już w kolejnych artykułach.</p>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2008/03/24/jquery-to-latwe-5-zakladki/feed/</wfw:commentRss>
		<slash:comments>40</slash:comments>
		</item>
		<item>
		<title>jQuery &#8211; to łatwe! #4</title>
		<link>http://ferrante.pl/2007/10/06/jquery-to-latwe-4/</link>
		<comments>http://ferrante.pl/2007/10/06/jquery-to-latwe-4/#comments</comments>
		<pubDate>Sat, 06 Oct 2007 18:23:36 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Vademecum]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://ferrante.pl/2007/10/06/jquery-to-latwe-4/</guid>
		<description><![CDATA[Poprzedni odcinek przyjemnego i praktycznego kursu jQuery pokazał nam, jak zacząć współpracę z elementami formularzy. Dziś pogłębimy nieco temat, doskonaląc skrypt znany z trzeciej części. Pojawi się także nowy, praktyczny i miejmy nadzieje użyteczny przykład, który pokaże, jak dodać i usunąć elementy formularza przy pomocy frameworka.
Standardowo, przypomnijmy sobie, na czym stoimy po lekturze trzeciej części [...]]]></description>
			<content:encoded><![CDATA[<p>Poprzedni odcinek przyjemnego i praktycznego kursu jQuery pokazał nam, jak zacząć współpracę z elementami formularzy. Dziś pogłębimy nieco temat, doskonaląc skrypt znany z trzeciej części. Pojawi się także nowy, praktyczny i miejmy nadzieje użyteczny przykład, który pokaże, jak dodać i usunąć elementy formularza przy pomocy frameworka.<span id="more-60"></span></p>
<p>Standardowo, przypomnijmy sobie, na czym stoimy po lekturze trzeciej części poradnika:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Strona z licznikiem znaków formularza&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf8" /&gt;
&lt;!-- dodanie jQuery do dokumentu --&gt;

&lt;script type="text/javascript" src="jQuery.js"&gt;&lt;/script&gt;

&lt;!-- Nasz kod Javascript: --&gt;

&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("form[name=formularz] textarea[name=content]").keyup(
		function()
		{
			$("#countChars").html($(this).val().length);
		});

	});
&lt;/script&gt;

&lt;/head&gt;
&lt;body&gt;
&lt;form name="formularz" action=""&gt;
	&lt;textarea name="content"&gt;&lt;/textarea&gt;
&lt;/form&gt;

&lt;p id="countChars"&gt;&lt;/p&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Wszystkie pięknie, problem polega jednak na tym, że większość naszych użytkowników nie pracuje w Urzędzie Statystycznym i z reguły niepotrzebna jest im informacja, ile znaków zapisali. O wiele lepszym pomysłem jest wprowadzenie limitu znaków, które można wpisać w <span class="f">textarea</span>. Ponadto możemy wyświetlić korzystającym z naszej strony informację, ile znaków pozostało im do wyczerpania limitu, co wydaje się znacznie rozsądniejsze.</p>
<p>Na początku należy ustalić jakikolwiek limit, który będzie zapisany w zmiennej <span class="f">limit</span> na początku skryptu:</p>
<pre><code>&lt;script type="text/javascript"&gt;

	var limit = 50;</code></pre>
<p>Jak widać, uznałem, że 50 znaków to będzie wystarczające maksimum. Oczywiście to od Ciebie zależy, jaką liczbę wpiszesz.</p>
<p>Mamy limit, pora na wykrycie momentu, kiedy użytkownik podczas pisania w <em>textarea</em> przekroczy limit znaków, ustalony przez nas wcześniej. Po chwili zastanowienia stwierdzamy, że limit zostaje przekroczony, jeśli liczba znaków wpisana do formularza jest większa od limitu. Wielu z Was zaświtała już pewnie prosta instrukcja warunkowa:</p>
<pre><code>if (liczba_znakow > limit) { spieprzaj dziadu... }</code></pre>
<p>No dobrze, mamy pomysł na to, jak rozpoznać, kiedy trzeba będzie po męsku rozprawić się z użytkownikiem i powiedzieć mu, że przekroczył właśnie podany limit. Pozostaje pytanie, co dalej? Otóż musimy z tego, co zostało wpisane do <em>textarea</em> wyciąć tyle znaków, ile wynosi limit. Jeśli limit to 50, wytniemy 50 pierwszych znaków, bo przecież kłóciłoby się z założeniem limitu. Kiedy to zrobimy, pora wkleić te 50 znaków do formularza, po uprzednim wykasowaniu wszystkich wpisanych znaków.</p>
<p>Jak dowiedzieliśmy się z poprzedniego odcinka, liczbę wpisanych znaków do elementu formularza można uzyskać według poniższego szablonu:</p>
<pre><code>$(selektor do elementu).val().length;</code></pre>
<p>Podstawiając to do warunku, który obmyśliliśmy sobie chwilę wcześniej, wyglądałoby to wszystko tak:</p>
<pre><code>if ($(this).val().length; > limit) { spieprzaj dziadu... }</code></pre>
<p>Umiejscowijmy to teraz w dokumencie:</p>
<pre><code>&lt;script type="text/javascript"&gt;

	var limit = 50;
	$(document).ready(
	function()
	{
		$("form[name=formularz] textarea[name=content]").keyup(
		function()
		{

			if ($(this).val().length; &gt; limit)
			{
				// tutaj dziad sobie spieprza...
			}
			$("#countChars").html($(this).val().length);
		});

	});
&lt;/script&gt;</code></pre>
<p>Ja, jako że lubię przejrzystość, zapisałbym liczbę wpisanych znaków w postaci klarownie nazwanej zmiennej, np. <span class="f">count_chars</span>:</p>
<pre><code>var count_chars = $(this).val().length;
if (count_chars &gt; limit)
{
	// nadal spieprza...
}</code></pre>
<p>Przejrzyściej? Oczywiście!</p>
<p>Jak sobie ustaliliśmy, musimy teraz pobrać tyle znaków z elementu <em>textarea</em>, ile wynosi maksimum.</p>
<pre><code>var count_chars = $(this).val().length;
if (count_chars &gt; limit)
{
	var new_value = $(this).val().substring(0, limit);
}</code></pre>
<div class="i">O funkcji <strong>substring</strong> możesz przeczytać na tym blogu w <em>Praktycznym wprowadzeniu do JavaScript</em>.</div>
<p>. W ten sposób, w zmiennej <span class="f">new_value</span> siedzi ciąg wpisanych przez użytkownika znaków, licząc od pierwszego do 50-tego (bo tyle wynosi zmienna <em>limit</em>). Zastosowana tutaj metoda jest bardzo prosta, najpierw pobieramy ciąg wszystkich wpisanych znaków do <em>textarea</em>(<em>$(this).val()</em>) &#8211; z pomocą przychodzi nam tutaj funkcja <em>val()</em>, która pobiera wartości formularzy w jQuery. Potem, dzięki funkcji JavaScript (<em>substring</em>), <em>obcinamy</em> go z prawej strony.</p>
<p>Czas na wklejenie tych 50 znaków do <em>textarea</em>. Z pomocą przychodzi ponownie funkcja <em>val()</em>, która oprócz tego, że pobiera, to także ustawia wartości elementom formularzy.</p>
<pre><code>var count_chars = $(this).val().length;
if (count_chars &gt; limit)
{
	var new_value = $(this).val().substring(0, limit);
	$(this).val(new_value);
}</code></pre>
<p>Jedyne, co nam zostało do zrobienia, to wyświetlenie liczby pozostałych znaków. Liczba ta równa się różnicy wartości limitu i wpisanych dotychczas znaków. Jeśli limit wynosi 50, a my wpisaliśmy dotychczas 20 znaków, to wyświetlić powinna się liczba 30 &#8211; wynik odejmowania 20 od 50.</p>
<p>Przyjmijmy, że informacja ta zapisana będzie w HTML w poniższej postaci:</p>
<pre><code>&lt;p id="countChars"&gt;Pozostalo &lt;span&gt;50&lt;/span&gt;  znaków&lt;/p&gt;</code></pre>
<p>Jeśli wpiszemy jeden znak, to w <em>&lt;span&gt;</em> widnieć będzie wartość 49 i tak dalej. Należy więc, dzięki jQuery, odnaleźć wszystkie tagi <em>span</em> w akapicie o <em>id=&#8221;countChars&#8221;</em>, a potem ustawić mu wartość odpowiadającą różnicy limitu i liczby wpisanych znaków.</p>
<pre><code>$("#countChars span")</code></pre>
<p>W ten sposób odnosimy się do wszystkich elementów <em>span</em> zawartych w elemencie o identyfikatorze <em>countChars</em>. Zawartość tych elementów, co już przerabialiśmy wcześniej, ustalimy dzięki funkcji <span class="f">html(&#8220;jakas wartosc&#8221;)</span>, co da w prostym przełożeniu <span class="f">$(&#8220;#countChars span&#8221;).html(&#8220;jakas wartosc&#8221;)</span> i efekt:</p>
<pre><code>&lt;p id="countChars"&gt;Pozostalo &lt;span&gt;jakas wartosc&lt;/span&gt;  znaków&lt;/p&gt;</code></pre>
<p>Pozostało nam wstawić wspominaną wcześniej różnicę znaków zamiast &#8220;jakas wartosc&#8221;:</p>
<pre><code>$("#countChars span").html(limit - $(this).val().length);</code></pre>
<p>Całość wygląda teraz tak i jest gotowa do działania:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Strona z licznikiem znaków formularza&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf8" /&gt;
&lt;!-- dodanie jQuery do dokumentu --&gt;

&lt;script type="text/javascript" src="jQuery.js"&gt;&lt;/script&gt;

&lt;!-- Nasz kod Javascript: --&gt;

&lt;script type="text/javascript"&gt;

	var limit = 5;
	$(document).ready(
	function()
	{
		$("form[name=formularz] textarea[name=content]").keyup(
		function()
		{
			var count_chars = $(this).val().length;
			if (count_chars &gt; limit)
			{
				var new_value = $(this).val().substring(0, limit);
				$(this).val(new_value)
			}
			$("#countChars span").html(limit - $(this).val().length);
		});

	});
&lt;/script&gt;

&lt;/head&gt;
&lt;body&gt;
&lt;form name="formularz" action=""&gt;
	&lt;textarea name="content"&gt;&lt;/textarea&gt;
&lt;/form&gt;

&lt;p id="countChars"&gt;Pozostalo &lt;span&gt;200&lt;/span&gt;  znaków&lt;/p&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Testując podany przykład natrafiamy jednak na mały problem &#8211; wszystko działa, dopóki nie wykorzystamy limitu znaków. Kiedy zostaje nam dokładnie 0 znaków do wpisania, pojawia się nam informacja, że wykorzystaliśmy &lt;puste miejsce&gt; znaków, zamiast 0 znaków. Dlaczego się tak dzieje?</p>
<p>Otóż jQuery interpretuje wartość 0 jako <em>null</em>, zero, nic. Myśli po prostu w kategoriach, w których zero traktujemy jak wartość logiczną, a nie jako wartość ciągową. Musimy więc dać do zrozumienia funkcji <em>html</em>, że podajemy ciąg znaków, a nie wartość logiczną. Jak wiadomo, ciąg znaków w JS otaczamy apostrofami bądź cudzysłowami. Poprawnym zapisem będzie więc coś takiego:</p>
<pre><code>$("#countChars span").html(""+(limit - $(this).val().length)+"");</code></pre>
<p><a href="http://ferrante.pl/examples/jquery/jQuery_4a.html">Oto gotowy przykład</a>:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Strona z licznikiem znaków formularza&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf8" /&gt;
&lt;!-- dodanie jQuery do dokumentu --&gt;

&lt;script type="text/javascript" src="jQuery.js"&gt;&lt;/script&gt;

&lt;!-- Nasz kod Javascript: --&gt;

&lt;script type="text/javascript"&gt;

	var limit = 5;
	$(document).ready(
	function()
	{
		$("form[name=formularz] textarea[name=content]").keyup(
		function()
		{
			var count_chars = $(this).val().length;
			if (count_chars &gt; limit)
			{
				var new_value = $(this).val().substring(0, limit);
				$(this).val(new_value)
			}
			$("#countChars span").html(""+limit - $(this).val().length+"");
		});

	});
&lt;/script&gt;

&lt;/head&gt;
&lt;body&gt;
&lt;form name="formularz" action=""&gt;
	&lt;textarea name="content"&gt;&lt;/textarea&gt;
&lt;/form&gt;

&lt;p id="countChars"&gt;Pozostalo &lt;span&gt;200&lt;/span&gt;  znaków&lt;/p&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3>Dodawanie i usuwanie elementów formularza</h3>
<p>Często chcemy umożliwić automatyczne dodawanie do dokumentów jakichś tagów. Najczęściej chodzi nam o elementy formularzy. Nierzadko, kiedy wypełnimy jakieś pole, ukazuje się nam automatycznie następne, którego obecność jest uzależniona od wpisania czegoś we wcześniejsze. I tak dalej. Zajmijmy się najpierw prostym dodawaniem różnego rodzaju <span class="f">inputów</span> po kliknięciu w link (wraz z możliwością ich usunięcia).</p>
<p>Dokument, na jakim będziemy pracować wygląda następująco:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Strona z dodawaniem i usuwaniem elementów formularza&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf8" /&gt;

&lt;style type="text/css"&gt;
label
{
	display: block;
}
label a
{
	color: #CC0000;
	text-decoration: none;
	margin-left: 5px;
}
label a:hover
{
	text-decoration: underline;
}
&lt;/style&gt;
&lt;!-- dodanie jQuery do dokumentu --&gt;

&lt;script type="text/javascript" src="jQuery.js"&gt;&lt;/script&gt;

&lt;!-- Nasz kod JavaScript: --&gt;

&lt;script type="text/javascript"&gt;

&lt;/script&gt;

&lt;/head&gt;
&lt;body&gt;
&lt;a href="#" id="dodaj"&gt;Dodaj element formularza&lt;/a&gt;
&lt;form name="formularz" action=""&gt;

&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Po kliknięciu w <em>Dodaj element formularza</em> formularz powinien mieć taką postać:</p>
<pre><code>&lt;form name="formularz" action=""&gt;
	&lt;label&gt;&lt;input type="text" /&gt;&lt;a href="#"&gt;usuń&lt;/a&gt;&lt;/label&gt;
&lt;/form&gt;</code></pre>
<p>Po drugim kliknięciu elementy <em>label</em> będą dwa, po trzecim trzy i tak dalej.</p>
<p>Jak zapewne się domyślasz, należy przechwycić moment kliknięcia w link o <em>id=&#8221;dodaj&#8221;</em>, potem stworzyć w JS tagi <em>label</em>, <em>input</em> i <em>a</em>, i dodać dwa ostatnie do utworzonego <em>labela</em>. Potem cały, wypełniony innymi tagami element <em>label</em> dodamy do formularza. </p>
<p>Zaczniemy standardowo:</p>
<pre><code>&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("#dodaj").click(function()
		{</code></pre>
<p>Po załadowaniu się drzewa DOM, skrypt znajdzie element o identyfikatorze <em>dodaj</em> i przypisze mu zdarzenie <em>click</em>, które oznacza kliknięcie tego elementu. Pora teraz zdefiniować, co ma wykonać się podczas tego zdarzenia. To natomiast uzgodniliśmy wcześniej &#8211; musimy utworzyć stosowne tagi i dodać je w określonej kolejności do odpowiednich, innych tagów.</p>
<p>Jak zapewne pamiętasz, elementy drzewa DOM (czyli właśnie tagi) tworzyliśmy poprzed funkcję <span class="f">document.createElement()</span>. Jeśli chcieliśmy stworzyć <em>div</em>, to pisaliśmy:</p>
<pre><code>var nowy_div = document.createElement("div");</code></pre>
<p>W jQuery nie musimy używać aż tak długiej konstrukcji. Przykładowo, tworzenie tagu <em>div</em> wygląda tak:</p>
<pre><code>var nowy_div = $("&lt;div&gt;");</code></pre>
<p>Wiedz, że można też użyć:</p>
<pre><code>var nowy_div = $(document.createElement("div"));</code></pre>
<p>Utwórzmy więc trzy pożądane elementy:</p>
<pre><code>&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("#dodaj").click(function()
		{
			var new_label = $("&lt;label&gt;");
			var new_input = $("&lt;input&gt;");
			var new_link = $("&lt;a&gt;");</code></pre>
<p>Każdy z powyższych elementów zapisany jest w oddzielnych zmiennych, dzięki którym będziemy mieli łatwy doń dostęp.</p>
<p>Pierwsze, co musimy zrobić, to określić, jaki atrybut <em>type</em> ma mieć nasz <em>input</em>. Uznajmy, że będzie to <em>text</em>. Jak więc go ustawić? Pomoże w tym nam funkcja <span class="f">attr()</span>, która podobnie jak <em>val()</em> pobiera i ustawia atrybuty danych tagów. Do roboty:</p>
<pre><code>&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("#dodaj").click(function()
		{
			var new_label = $("&lt;label&gt;");
			var new_input = $("&lt;input&gt;");
			new_input.attr("type", "text");
			var new_link = $("&lt;a&gt;");</code></pre>
<p>Wyróżnijmy fragment, który poddany został modyfikacjom:</p>
<pre><code>var new_input = $("&lt;input&gt;");
new_input.attr("type", "text");</code></pre>
<div class="i">Funkcja <strong>attr</strong> może przyjmować jeden lub dwa argumenty. W pierwszym wypadku, podajemy nazwę atrybutu, którego wartość chcemy uzyskać, np.: <em>$(&#8220;a&#8221;).attr(&#8220;href&#8221;);</em>. W drugim, pierwszy argument to nazwa atrybutu, drugi to przypisywana do niego nowa wartość, np.: <em>$(&#8220;a&#8221;).attr(&#8220;id&#8221;, &#8220;costam&#8221;);</em>.</div>
<p>Jak widzisz, mając w zmiennej <span class="f">new_input</span> odnośnik do utworzonego elementu formularza, nadajemy mu, dzięki funkcji <em>attr</em>, atrybut <em>type</em> równy <em>text</em>. Pewnie Cię dziwi brak <em>$()</em>, ale spójrz, co zawiera ta zmienna, a wszystko stanie się prostsze. To tak, jakbyś napisał:</p>
<pre><code>$("&lt;input&gt;").attr("type", "text");</code></pre>
<p>My natomiast używamy zmiennych, ponieważ okażą się one bardzo pomocne, kiedy będziemy dodawać poszczególne elementy do innych tagów.</p>
<p>Pora zająć się dodawanym linkiem. Jako że jest on całkowicie <em>goły i wesoły</em>, nadamy mu atrybut <em>href</em> i jakąś zawartość pomiędzy tagami, niech to będzie <em>usuń</em>.</p>
<pre><code>&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("#dodaj").click(function()
		{
			var new_label = $("&lt;label&gt;");
			var new_input = $("&lt;input&gt;");
			new_input.attr("type", "text");
			var new_link = $("&lt;a&gt;");
			new_link.attr("href", "#");
			new_link.html("usuń");</code></pre>
<p>Pora teraz sprawić, że link i <em>input</em> zostaną dodane do nowego elementu <em>label</em> (który mieści się w zmiennej <em>new_label</em>), a ten, już nimi uzupełniony, trafi do całego formularza. Pomoże nam w tym funkcja <span class="f">append()</span>, która użyta z jakimś elementem dodaje na jego koniec to, co umieścimy w argumencie. Np.:</p>
<pre><code>$("p#akapit").append("&lt;a&gt;cos&lt;/a&gt;");</code></pre>
<p>doda do akapitu o id=&#8221;akapit&#8221; link o treści &#8220;cos&#8221;. Odnośnik ten trafi na sam koniec drzewa dzieci akapitu.</p>
<pre><code>&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("#dodaj").click(function()
		{
			var new_label = $("&lt;label&gt;");
			var new_input = $("&lt;input&gt;");
			new_input.attr("type", "text");
			var new_link = $("&lt;a&gt;");
			new_link.attr("href", "#");
			new_link.html("usuń");

			new_label.append(new_input);
			new_label.append(new_link);
			$("form[name=formularz]").append(new_label);

		});

	});
&lt;/script&gt;</code></pre>
<p>Dodaliśmy to:</p>
<pre><code>new_label.append(new_input);
new_label.append(new_link);
$("form[name=formularz]").append(new_label);</code></pre>
<p>Pierwsza linijka dodaje do <em>label</em> nasz nowy <em>input</em>, druga link, a trzecia dodaje do formularza o <em>name=&#8221;formularz&#8221;</em> element <em>label</em> wraz z jego zawartością.</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Strona z dodawaniem i usuwaniem elementów formularza&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf8" /&gt;

&lt;style type="text/css"&gt;
label
{
	display: block;
}
label a
{
	color: #CC0000;
	text-decoration: none;
	margin-left: 5px;
}
label a:hover
{
	text-decoration: underline;
}
&lt;/style&gt;
&lt;!-- dodanie jQuery do dokumentu --&gt;

&lt;script type="text/javascript" src="jQuery.js"&gt;&lt;/script&gt;

&lt;!-- Nasz kod JavaScript: --&gt;

&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("#dodaj").click(function()
		{
			var new_label = $("&lt;label&gt;");
			var new_input = $("&lt;input&gt;");
			new_input.attr("type", "text");
			var new_link = $("&lt;a&gt;");
			new_link.attr("href", "#");
			new_link.html("usuń");

			new_label.append(new_input);
			new_label.append(new_link);
			$("form[name=formularz]").append(new_label);

		});

	});
&lt;/script&gt;

&lt;/head&gt;
&lt;body&gt;
&lt;a href="#" id="dodaj"&gt;Dodaj element formularza&lt;/a&gt;
&lt;form name="formularz" action=""&gt;

&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Otrzymaliśmy coś takiego, jak powyżej. Po raz kolejny można mieć jednak kilka zastrzeżeń, co do działania całości. Po pierwsze, link <em>usuń</em> nie usuwa całego <em>labela</em>, który jest jego rodzicem i zawiera <em>input</em> do wywalenia. Pora to zmienić. Znajdujemy więc linijkę, gdzie tworzymy nowy link. Musimy mu nadać zdarzenie <em>click</em>. Podczas tego zdarzenia, znajdziemy jego rodzica (funkcja <span class="f">parent()</span>), który musi być tagiem typu <em>label</em>. Dzięki funkcji <span class="f">remove()</span> usuniemy go z drzewa DOM.</p>
<pre><code>new_link.click(function()
{
	$(this).parent("label").remove();
});</code></pre>
<p>Teraz pozostaje tylko dodać na sam koniec skryptu <em>return false;</em> by zablokować ewentualne przechodzenie do adresu linka <em>Dodaj nowy element</em>:</p>
<pre><code>&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("#dodaj").click(function()
		{
			var new_label = $("&lt;label&gt;");
			var new_input = $("&lt;input&gt;");
			new_input.attr("type", "text");
			var new_link = $("&lt;a&gt;");
			new_link.attr("href", "#");
			new_link.html("usuń");
			new_link.click(function()
			{
				$(this).parent("label").remove();
			});

			new_label.append(new_input);
			new_label.append(new_link);
			$("form[name=formularz]").append(new_label);
			return false;
		});

	});
&lt;/script&gt;</code></pre>
<p>To samo można zrobić, z usuwaniem <em>labela</em>:</p>
<pre><code>new_link.click(function()
{
	$(this).parent("label").remove();
	return false;
});</code></pre>
<p><a href="http://ferrante.pl/examples/jquery/jQuery_4b.html">Działa, jak należy</a>!</p>
<h3>Wycinek z dokumentacji</h3>
<p>W każdym odcinku znajdą się bardzo proste i dużo pokazujące przykłady funkcji z danego odcinka, zaczerpnięte z oficjalnej dokumentacji jQuery. Wielokrotnie, jak znam życie, spojrzysz najpierw tutaj, nim weźmiesz się za tekst całego kursu i jest to dobre podejście. Kod jQuery jest często dość nieczytelny i może wydawać się zagmatwany. Proste, krótkie przykłady to zaś dobry sposób na uzmysłowienie sobie działania funkcji.</p>
<h3>parent(filtr)</h3>
<p>Funkcja użyta w kontekście danego elementu pobiera jego rodzica, który jest regulowany przez podany filtr w postaci selektora, tagu etc&#8230;</p>
<p>Kod:</p>
<pre><code>&lt;p&gt;&lt;a href="ferrante.pl" id="link"&gt;ferrante blog&lt;/a&gt;&lt;/p&gt;</code></pre>
<pre><code>$("#link").parent("p");</code></pre>
<p>Zwróci:</p>
<pre><code>&lt;p&gt;&lt;/p&gt;</code></pre>
<h3>append(element DOM)</h3>
<p>Funkcja użyta w kontekście danego elementu dodaje doń nowy element drzewa DOM.</p>
<p>Kod:</p>
<pre><code>&lt;p&gt;&lt;a href="ferrante.pl" id="link"&gt;ferrante blog&lt;/a&gt;&lt;/p&gt;</code></pre>
<pre><code>$("#link").append("&lt;strong&gt;Tak, to jest blog Ferrante&lt;/strong&gt;");</code></pre>
<p>Zwróci:</p>
<pre><code>&lt;p&gt;&lt;a href="ferrante.pl" id="link"&gt;ferrante blog &lt;strong&gt;Tak, to jest blog Ferrante&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;</code></pre>
<h3>attr(nazwa atrybutu, nowa wartość atrybutu)</h3>
<p>Funkcja użyta w kontekście danego elementu z pierwszym argumentem &#8211; pobiera wartość atrybutu o podanej nazwie, z dwoma &#8211; ustala nową wartość atrybutowi o podanej nazwie.</p>
<p>Kod:</p>
<pre><code>&lt;p&gt;&lt;/p&gt;</code></pre>
<pre><code>$("p").attr("id", "akapit");</code></pre>
<p>Zwróci:</p>
<pre><code>&lt;p id="akapit"&gt;&lt;/p&gt;</code></pre>
<p>Kod:</p>
<pre><code>&lt;p class="akapit"&gt;&lt;/p&gt;</code></pre>
<pre><code>$("p").attr("class");</code></pre>
<p>Zwróci:</p>
<pre><code>akapit</code></pre>
<h3>remove(filtr)</h3>
<p>Funkcja użyta w kontekście danego elementu usuwa go z drzewa elementów DOM.</p>
<p>Kod:</p>
<pre><code>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;</code></pre>
<pre><code>$("p").remove("span");</code></pre>
<p>Zwróci:</p>
<pre><code>&lt;p&gt;&lt;/p&gt;</code></pre>
<p>Kod:</p>
<pre><code>&lt;p&gt;&lt;/p&gt;</code></pre>
<pre><code>$("p").remove();</code></pre>
<p>Zwróci:</p>
<pre><code>...</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2007/10/06/jquery-to-latwe-4/feed/</wfw:commentRss>
		<slash:comments>30</slash:comments>
		</item>
		<item>
		<title>jQuery &#8211; to łatwe! #3</title>
		<link>http://ferrante.pl/2007/08/07/jquery-to-latwe-3/</link>
		<comments>http://ferrante.pl/2007/08/07/jquery-to-latwe-3/#comments</comments>
		<pubDate>Tue, 07 Aug 2007 16:53:47 +0000</pubDate>
		<dc:creator>ferrante</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Vademecum]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://ferrante.pl/2007/08/07/jquery-to-latwe-3/</guid>
		<description><![CDATA[W poprzednim odcinku poznaliśmy kilka sposobów na przyjemną i szybką manipulację DOM. Powstał dość zgrabny przykład rozsuwanego menu. Dzisiaj ulepszymy go o efektowne animacje, a także popracujemy trochę na formularzach.
Na początek przypomnijmy, jakim kodem dysponujemy:
&#60;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&#62;
&#60;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&#62;
&#60;head&#62;
&#60;title&#62;Strona z nieuporządkowaną listą&#60;/title&#62;
&#60;meta http-equiv="Content-Type" content="text/html; charset=utf8" /&#62;

&#60;style type="text/css"&#62;
	ul
	{
		list-style-type: none;
	}
	li.highlight
	{
		background: #000;
		color: #fff;
	}
	ul#lista [...]]]></description>
			<content:encoded><![CDATA[<p>W poprzednim odcinku poznaliśmy kilka sposobów na przyjemną i szybką manipulację DOM. Powstał dość zgrabny przykład rozsuwanego menu. Dzisiaj ulepszymy go o efektowne animacje, a także popracujemy trochę na formularzach.<span id="more-59"></span></p>
<p>Na początek przypomnijmy, jakim kodem dysponujemy:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Strona z nieuporządkowaną listą&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf8" /&gt;

&lt;style type="text/css"&gt;
	ul
	{
		list-style-type: none;
	}
	li.highlight
	{
		background: #000;
		color: #fff;
	}
	ul#lista li p
	{
		display: none;
	}
&lt;/style&gt;

&lt;!-- dodanie jQuery do dokumentu --&gt;
&lt;script type="text/javascript" src="jQuery.js"&gt;&lt;/script&gt;

&lt;!-- Nasz kod Javascript: --&gt;

&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("ul#lista li").children("h2").click( function() { $(this).next("p").toggle(); });
	});
&lt;/script&gt;

&lt;/head&gt;
&lt;body&gt;
	&lt;ul id="lista"&gt;
		&lt;li&gt;&lt;h2&gt;Klik&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin vitae mi. Proin dignissim ligula vitae metus. Etiam imperdiet. Aenean nonummy lorem ut quam.&lt;/p&gt;&lt;/li&gt;
		&lt;li&gt;&lt;h2&gt;Klik&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin vitae mi. Proin dignissim ligula vitae metus. Etiam imperdiet. Aenean nonummy lorem ut quam.&lt;/p&gt;&lt;/li&gt;
		&lt;li&gt;&lt;h2&gt;Klik&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin vitae mi. Proin dignissim ligula vitae metus. Etiam imperdiet. Aenean nonummy lorem ut quam.&lt;/p&gt;&lt;/li&gt;
		&lt;li&gt;&lt;h2&gt;Klik&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin vitae mi. Proin dignissim ligula vitae metus. Etiam imperdiet. Aenean nonummy lorem ut quam.&lt;/p&gt;&lt;/li&gt;
	&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Powyższy przykład umożliwia rozwijanie i zwijanie akapitów należących do danego elementu listy nieuporządkowanej po kliknięciu w nagłówek drugiego stopnia, należącego do tego samego elementu.</p>
<p>Testując skrypt dochodzimy jednak do wniosku, że w ten sposób możemy rozwinąć wszystkie cztery akapity, które potem będą jednocześnie widoczne. Dobrym pomysłem w tej sytuacji jest efekt zakładek. O co chodzi? Otóż, kiedy klikniemy pierwszy raz w nagłówek &#8211; wszystko będzie w porządku. Jeśli klikniemy drugi raz w inny nagłówek &#8211; widoczne są jednocześnie dwa akapity. Dzięki efektowi zakładek widoczny będzie tylko ten, który przed chwilą odsłoniliśmy. &#8220;Stary&#8221; akapit zostanie skrzętnie ukryty wraz z kliknięciem sygnalizującym pokazanie innego. Wszystko działa w ten sposób, jakbyśmy mieli do czynienia z zakładkami, gdzie tylko jedna może być aktywna.</p>
<p>Przedstawmy symulację działania skryptu:</p>
<ul>
<li>Klikamy w dowolny nagłówek.</li>
<li>Pokazujemy akapit sąsiadujący z tym nagłówkiem.</li>
<li>Następnie pobieramy wszystkie nagłówki z wyjątkiem tego, który kliknęliśmy.</li>
<li>Pobieramy sąsiadujące z tymi nagłówkami, widoczne akapit. W praktyce będzie to zawsze tylko jeden akapit.</li>
<li>Ukrywamy je, np. używając funkcji <span class="f">hide</span>.</li>
</ul>
<p>To dość proste zadanie, o czym przekonamy się za chwilę. Pierwsze 2 punkty zrealizowaliśmy w 2. odcinku naszych zmagań z jQuery. Pora zająć się kolejnymi trzema.</p>
<p>Wszystkie z nich musimy wykonać w momencie kliknięcia w dowolny nagłówek, ściślej rzecz biorąc podczas wykonywania zdarzenia <span class="f">click</span>, czyli tam, gdzie teraz pokazujemy sąsiadujące akapity:</p>
<pre><code>&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("ul#lista li").children("h2").click( function() { $(this).next("p").toggle(); <strong>// o, tutaj!</strong> });
	});
&lt;/script&gt;</code></pre>
<p>Warto też od razu zauważyć, że uruchamiając na każdym akapicie funkcję <em>toggle</em>, umożliwiamy jego pokazanie, jak i ukrycie. Zwijać akapity będziemy jednak, jak już ustaliliśmy, gdzie indziej, więc zamieńmy funkcję <em>toggle</em> na <em>show</em> z parametrem <em>normal</em> (normalna szybkość animacji pokazywania się akapitu).</p>
<pre><code>&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("ul#lista li").children("h2").click( function() { $(this).next("p").show("normal"); });
	});
&lt;/script&gt;</code></pre>
<p>No dobrze, pora na szybkie i efektowne działanie, jak to z reguły w jQuery ma miejsce. Wiemy już, w jakim miejscu zrealizujemy nasze nowe cele odnośnie działania skryptu. Pora zamienić nasze pomysły na kod.</p>
<p>Pobierzmy więc wszystkie nagłówki należące do elementów listy, korzystając z prostych selektorów:</p>
<pre><code>$("ul#lista li h2")</code></pre>
<p>Jeśli znasz CSS nie powinieneś mieć problemów z rozszyfrowaniem, do jakich elementów chcemy się dostać. Zaskoczonym podpowiem jedynie, że chodzi o nagłówki drugiego stopnia, będące elementami nieuporządkowanej listy o <em>id=&#8221;lista&#8221;</em>. </p>
<p>Przy tej okazji, przypomniała mi się też inna kwestia. W linijce powyżej, gdzie nadawaliśmy zdarzenie <em>click</em> każdemu nagłówkowi <em>h2</em>, użyliśmy do ich pobrania poniższej konstrukcji:</p>
<pre><code>$("ul#lista li").children("h2")</code></pre>
<p>Można to zastąpić czymś takim (spróbujemy pobrać wszystkie dzieci <em>h2</em> niezależnie od tego, jak głęboko zagnieżdzony jest element <em>h2</em> &#8211; funkcja <em>children</em> pobiera tylko bezpośrednich &#8220;potomków&#8221;).</p>
<pre><code>$("ul#lista li h2")</code></pre>
<p>Jak widzisz, jQuery jest bardzo elastyczne, co po raz kolejny udowodniło powyżej. Przejdźmy jednak do naszego skryptu. Musimy wykluczyć pobieranie nagłówka, w który właśnie kliknęliśmy. Jak zapewne dostrzegłeś w drugiej części serii artykułów o jQuery, mogliśmy się do niego dostać stosując zmienną <em>this</em>, o tutaj:</p>
<pre><code>$("ul#lista li").children("h2").click( function() { $(<strong>this</strong>).next("p").toggle(); });</code></pre>
<p>Aby wykluczyć jego obecność, posłużymy się właśnie <em>this</em>. Nie będzie on jednak zastosowany samotnie, a w duecie z funkcją <span class="f">not</span>, której użycie zapewnia nam to, że jQuery nie pobierze podanych jej elementów. Popatrzmy najpierw na oderwany od kontekstu przykład jej działania. Mamy poniższą strukturę:</p>
<pre><code>&lt;p class="active"&gt;a&lt;/p&gt;
&lt;p id="element-2"&gt;b&lt;/p&gt;
&lt;p&gt;c&lt;/p&gt;
&lt;p class="active"&gt;d&lt;/p&gt;</code></pre>
<p>Powiedzmy, że chcemy wybrać wszystkie akapity oprócz tych, które mają klasę równą <span class="f">active</span>. Użyjemy wtedy:</p>
<pre><code>$("p").not(".active");</code></pre>
<p>Wtedy pobrane zostaną tylko poniższe elementy:</p>
<pre><code>&lt;p id="element-2"&gt;b&lt;/p&gt;
&lt;p &gt;c&lt;/p&gt;</code></pre>
<p>Podobny manewr możemy zastosować z identyfikatorami:</p>
<pre><code>$("p").not("#active");</code></pre>
<p>Czy też dowolnymi tagami:</p>
<pre><code>$("p").not("jakis_tag");</code></pre>
<p>Wracając do naszego przykładu, możemy łatwo wyeliminować pobieranie aktywnego nagłówka podstawiając funkcji jako argument zmienną <em>this</em>, wskazującą właśnie nań.</p>
<p>Powstało coś takiego:</p>
<pre><code>$("ul#lista li h2").not(this)</code></pre>
<p>W ten sposób, gdybyśmy kliknęli w drugi nagłówek, pobrane byłyby tylko pierwszy, trzeci i czwarty.</p>
<p>Pora teraz dostać się do akapitów, sąsiadujących z tymi nagłówkami. Użyjemy do tego starej, dobrej funkcji <span class="f">next</span>.</p>
<pre><code>$("ul#lista li h2").not(this).next("p")</code></pre>
<p>Ba, skrypt można jeszcze bardziej zoptymalizować, tak, by funkcja <em>next</em> pobierała tylko widoczne akapity. Użyjemy do tego selektoru <span class="f">:visible</span>:</p>
<pre><code>$("ul#lista li h2").not(this).next("p:visible")</code></pre>
<p><span class="f">p:visible</span> powoduje wyszukanie tylko tych, które są aktualnie widoczne.</p>
<p>Pora teraz na ich schwanie. Używamy tutaj funkcji <span class="f">hide()</span> z atrybutem <span class="f">normal</span>, określając tym samym szybkość animacji ukrywania:</p>
<pre><code>$("ul#lista li h2").not(this).next("p:visible").hide("normal");</code></pre>
<p>I jest! Pora wstawić powyższą linijkę do zdarzenia <em>click</em>, bo tam należy chować inne, niż aktywny, akapity.</p>
<pre><code>&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("ul#lista li").children("h2").click( function() { $(this).next("p").show("normal"); $("ul#lista li h2").not(this).next("p:visible").hide("normal"); });
	});
&lt;/script&gt;</code></pre>
<p>Wygląda na to, że <a href="http://ferrante.pl/examples/jquery/jQuery_3a.html">działa</a>. Cały dokument wygląda natomiast tak, jak poniżej:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Strona z nieuporządkowaną listą&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf8" /&gt;

&lt;style type="text/css"&gt;
	ul
	{
		list-style-type: none;
	}
	li.highlight
	{
		background: #000;
		color: #fff;
	}
	ul#lista li p
	{
		display: none;
	}
&lt;/style&gt;

&lt;!-- dodanie jQuery do dokumentu --&gt;
&lt;script type="text/javascript" src="jQuery.js"&gt;&lt;/script&gt;

&lt;!-- Nasz kod Javascript: --&gt;

&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("ul#lista li").children("h2").click(
		function()
		{ 

			$(this).next("p").show("normal");
			$("ul#lista li h2").not(this).next("p:visible").hide("normal"); 

		}
		);
	}
	);
&lt;/script&gt;

&lt;/head&gt;
&lt;body&gt;
	&lt;ul id="lista"&gt;
		&lt;li&gt;&lt;h2&gt;Klik&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin vitae mi. Proin dignissim ligula vitae metus. Etiam imperdiet. Aenean nonummy lorem ut quam.&lt;/p&gt;&lt;/li&gt;
		&lt;li&gt;&lt;h2&gt;Klik&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin vitae mi. Proin dignissim ligula vitae metus. Etiam imperdiet. Aenean nonummy lorem ut quam.&lt;/p&gt;&lt;/li&gt;
		&lt;li&gt;&lt;h2&gt;Klik&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin vitae mi. Proin dignissim ligula vitae metus. Etiam imperdiet. Aenean nonummy lorem ut quam.&lt;/p&gt;&lt;/li&gt;
		&lt;li&gt;&lt;h2&gt;Klik&lt;/h2&gt;&lt;p&gt;Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin vitae mi. Proin dignissim ligula vitae metus. Etiam imperdiet. Aenean nonummy lorem ut quam.&lt;/p&gt;&lt;/li&gt;
	&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Warto dodać, że zamiast funkcji <span class="f">show</span> i <span class="f">hide</span> możemy użyć kolejno <span class="f">slideDown</span> (rozwijanie) i <span class="f">slideUp</span> (zwijanie). Różni je to, że <em>slide&#8217;y</em> nie obsługują przezroczystości w animacjach &#8211; zmieniają tylko <em>height</em> elementów, na których działają. Obie funkcje przyjmują te same argumenty jak <em>show</em>, czy <em>hide</em> &#8211; <em>slow</em>, <em>normal</em>, <em>fast</em> lub dowolna liczba milisekund. Można również jako drugi argument podać jakąś funkcję, która uruchomi się po zakończeniu zwijania bądź rozwijania.</p>
<p>Co jeszcze ważne, a czego człowiek dowiaduje się często dopiero z dokumentacji &#8211; domyślnym parametrem oznaczającym szybkość wykonywania się funkcji <em>slideDown</em> i <em>slideUp</em> &#8211; jest <em>normal</em>. Jeśli więc wywołamy funkcję bez argumentu, np.:</p>
<pre><code>$("p").slideDown();</code></pre>
<p>Wszyskie akapity rozwiną się tak, jakbyśmy jako argument podali <em>slideDown(&#8220;normal&#8221;)</em>.</p>
<p>Pobawiliśmy się już trochę funkcjami operującymi na zwykłych tagach. Zajmijmy się teraz formularzami, choć warto zaznaczyć, że do podobnych przykładów, jak ten jeszcze wrócimy.</p>
<h3>Formularze w jQuery</h3>
<p>Dzięki łatwym dostępie do różnorakich gałęzi drzewka tagów poprzez funkcję <em>$()</em>, zabawa z formularzami jest niezwykle prosta. W JS, dobierając się do wszystkich elementów formularza o nazwie <span class="f">formularz</span>, użylibyśmy poniższego kodu:</p>
<pre><code>document.forms['formularz'].elements;</code></pre>
<p>W jQuery sprawa opiera się na niewielu znakach:</p>
<pre><code>$("formularz.elements");</code></pre>
<p>Zaczynając naszą przygodę z formularzami w jQuery, zajmijmy się na początek licznikiem znaków wpisanych do formularza. Tego typu skrypty od zawsze cieszyły się sporą popularnością, a dzięki jQuery nie trzeba szukać długo &#8211; kod to tylko kilka linijek. Przyjmijmy więc, że mamy poniższą strukturę:</p>
<pre><code>&lt;form name="formularz" action=""&gt;
	&lt;textarea name="content" &gt;&lt;/textarea&gt;
&lt;/form&gt;

&lt;p id="countChars"&gt;&lt;/p&gt;</code></pre>
<p>Załóżmy, że podczas wpisywania znaków w polu <span class="f">content</span>, ich liczba będzie pokazywana w akapicie o <em>id</em> równym <span class="f">countChars</span>. Trzeba będzie więc wychwycić, kiedy zwolnimy jakiś klawisz klawiatury podczas wpisywania treści. Jak już wiemy z JavaScript, za coś podobnego odpowiada zdarzenie <em>onkeyup</em>. W jQuery twórcy przewidzieli nazwę <em>keyup</em>, czyli w zasadzie podobnie, jak to miało miejsce w wypadku <em>onclick</em> (w jQuery <em>click</em>).</p>
<p>Kiedy będziemy wiedzieć, że nacisnęliśmy jakiś klawisz w polu <em>content</em>, policzymy jego znaki. Uzyskaną wartością uzupełnimy akapit. Pora zaczynać! Oczywiście zezwalamy jQuery na działanie tuż po załadowaniu drzewa DOM:</p>
<pre><code>$(document).ready(
	function()
	{</code></pre>
<p>Następnie, by dodać jakieś zdarzenie elementowi <em>textarea</em> musimy się do niego dostać. W CSS wyglądałoby to tak:</p>
<pre><code>form[name="formularz"] textarea[name="content"] {}</code></pre>
<p>W jQuery również możliwe jest użycie powyższych selektorów:</p>
<pre><code>$("form[name=formularz] textarea[name=content]")</code></pre>
<p>W ten sposób pobraliśmy odnośnik do elementu formularza o nazwie <em>formularz</em>, który nosi nazwę <em>content</em>. Możemy w ten sposób wykonywać na nim różne operacje. Nie będziemy jednak rozmyślać jakie, bo już to sobie ustaliliśmy &#8211; trzeba najpierw przechwycić zdarzenie zwolnienia przycisku klawiatury z tego pola, co będzie oznaczało, że użytkownik wpisał bądź usunął jeden znak:</p>
<pre><code>$(document).ready(
	function()
	{
		$("form[name=formularz] textarea[name=content]").keyup(
		function()
		{</code></pre>
<p>Jak widać, robimy to podobnie, jak w przypadku <em>click</em>.</p>
<p>Mamy już prawie cały skrypt, pora teraz na podliczenie ilości znaków dla elementu i umieszczenie jej w akapicie:</p>
<pre><code>$(document).ready(
	function()
	{
		$("form[name=formularz] textarea[name=content]").keyup(
		function()
		{
			$("#countChars").html($(this).val().length);
		});

	});</code></pre>
<p>Wszystko za nas robi poniższa linijką, którą postaramy się przeanalizować:</p>
<pre><code>$("#countChars").html($(this).val().length);</code></pre>
<p>Na początku, by uzupełnić akapit ilością znaków z <em>textarea</em>, musimy się doń dostać, stąd nie dziwi <em>$(&#8220;#countChars&#8221;)</em>. Następnie uruchamiamy na pobranym akapicie funkcję <span class="f">html</span>, która działa podobnie jak <em>text</em>, tyle że podany jej tekst jest interpretowany jako html.</p>
<p>Działanie funkcji <em>text</em> i <em>html</em> możemy porównać w takim przykładzie:</p>
<pre><code>$("p").text("&lt;strong&gt;costam&lt;/strong&gt;");</code></pre>
<pre><code>$("p").html("&lt;strong&gt;costam&lt;/strong&gt;");</code></pre>
<p>Pierwszy da nam:</p>
<pre><code>&lt;p&gt;&lt;strong&gt;costam&lt;/strong&gt;&lt;/p&gt;</code></pre>
<p>Drugi:</p>
<pre><code>&lt;p&gt;<strong>costam</strong>&lt;/p&gt;</code></pre>
<p>Jak widać, funkcja <em>text</em> formatuje podaną wartość do czystego tekstu, zaś <em>html</em> do html&#8217;a.</p>
<p>Wracając do naszej analizy, w środku funkcji <em>html</em> ustawiamy wartość akapitu jako</p>
<pre><code>$(this).val().length</code></pre>
<div class="i"><strong>val()</strong><br />
Funkcja ta użyta bez argumentów w kontekście elementu formularza zwraca jego wartość atrybutu <em>value</em>.</div>
<p>Odwołujemy się tutaj do znalezionego elementu <em>textarea</em> (<em>$(this)</em>), następnie do jego wartości (<span class="f">val()</span>). Używając <em>length</em> liczymy ilość znaków, jakie zwróciła sąsiednia funkcja <em>val()</em>. Czyli liczbę wpisanych znaków. W JS robiliśmy to wielokrotnie. <a href="http://ferrante.pl/examples/jquery/jQuery_3b.html">Zobacz przykład</a>!</p>
<p>Powstały dokument:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" lang="pl-PL"&gt;
&lt;head&gt;
&lt;title&gt;Strona z licznikiem znaków formularza&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf8" /&gt;
&lt;!-- dodanie jQuery do dokumentu --&gt;

&lt;script type="text/javascript" src="jQuery.js"&gt;&lt;/script&gt;

&lt;!-- Nasz kod Javascript: --&gt;

&lt;script type="text/javascript"&gt;

	$(document).ready(
	function()
	{
		$("form[name=formularz] textarea[name=content]").keyup(
		function()
		{
			$("#countChars").html($(this).val().length);
		});

	});
&lt;/script&gt;

&lt;/head&gt;
&lt;body&gt;
&lt;form name="formularz" action=""&gt;
	&lt;textarea name="content"&gt;&lt;/textarea&gt;
&lt;/form&gt;

&lt;p id="countChars"&gt;&lt;/p&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Naszą zabawę z formularzami dokończymy w następnym odcinku, gdzie będzie co nieco o blokadzie znaków, walidacji i tym podobnych efektach.</p>
<h3>Wycinek z dokumentacji</h3>
<p>W każdym odcinku znajdą się bardzo proste i dużo pokazujące przykłady funkcji z danego odcinka, zaczerpnięte z oficjalnej dokumentacji jQuery. Wielokrotnie, jak znam życie, spojrzysz najpierw tutaj, nim weźmiesz się za tekst całego kursu i jest to dobre podejście. Kod jQuery jest często dość nieczytelny i może wydawać się zagmatwany. Proste, krótkie przykłady to zaś dobry sposób na uzmysłowienie sobie działania funkcji.</p>
<h3>not(filtr)</h3>
<p>Funkcja użyta w kontekście pobierania przez jQuery elementu bądź elementów wyklucza jego pobranie, jeśli taki pasuje do podanego filtru.</p>
<p>Kod:</p>
<pre><code>&lt;li class="a"&gt;a&lt;/li&gt;
&lt;li&gt;b&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;</code></pre>
<pre><code>$("li").not(".a");</code></pre>
<p>Zwróci:</p>
<pre><code>&lt;li&gt;b&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;</code></pre>
<h3>slideDown(szybkość, funkcja)</h3>
<p>Funkcja użyta w kontekście danego elementu rozwija go z szybkością podaną w pierwszym argumencie. Może to być <em>slow</em>, <em>normal</em>, <em>fast</em> bądź dowolna liczba milisekund. Funkcję tę można również uruchomić z drugim argumentem w postaci funkcji, która wykona się po wykonaniu <em>slideDown</em>, czyli rozwinięciu elementu. Funkcja <em>slideDown</em> jest analogiczna do <em>hide</em>, <em>show</em> czy też <em>toggle</em>. W swojej animacji operuje jednak tylko na wysokości elementu.</p>
<h3>slideUp(szybkość, funkcja)</h3>
<p>Funkcja użyta w kontekście danego elementu zwija go z szybkością podaną w pierwszym argumencie. Może to być <em>slow</em>, <em>normal</em>, <em>fast</em> bądź dowolna liczba milisekund. Funkcję tę można również uruchomić z drugim argumentem w postaci funkcji, która uruchomi się po wykonaniu <em>slideDown</em>, czyli rozwinięciu elementu. Funkcja <em>slideDown</em> jest analogiczna do <em>hide</em>, <em>show</em> czy też <em>toggle</em>. W swojej animacji operuje jednak tylko na wysokości elementu.</p>
<h3>html(tekst)</h3>
<p>Funkcja użyta w kontekście danego elementu (tagu), uzupełnia jego wartość, interpretując podany tekst jako HTML.</p>
<p>Kod:</p>
<pre><code>$("p").html("&lt;em&gt;tekst&lt;/em&gt;");</code></pre>
<p>Zwróci:</p>
<pre><code>&lt;p&gt;<em>tekst</em>&lt;/p&gt;</code></pre>
<h3>val()</h3>
<p>Funkcja użyta w kontekście danego elementu formularza, zwraca wartość jego atrybutu <em>value</em>.</p>
<p>Kod:</p>
<pre><code>&lt;input type="text" value="Cos tam" /&gt;</code></pre>
<pre><code>$("input[type=text]").val();</code></pre>
<p>Zwróci:</p>
<pre><code>Cos tam</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://ferrante.pl/2007/08/07/jquery-to-latwe-3/feed/</wfw:commentRss>
		<slash:comments>52</slash:comments>
		</item>
	</channel>
</rss>
