Morrison M. - Head First JavaScript. Edycja polska

Spis treści 5 Spis treści (z prawdziwego zdarzenia) Wprowadzenie Twój mózg koncentruje się na JavaScripcie. Siedzisz, próbując się czegoś nauczyć, ale...

14 downloads 19 Views 33MB Size


jesteś tutaj 

39

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie

Wiesz więcej, niż sam zdajesz sobie z tego sprawę. Przyjrzyj się kodowi strony Wyszukiwarka domów i zapisz, do czego według Ciebie służy każdy z zakreślonych fragmentów kodu JavaScript. Nie bój się zgadywać.

Wyszukiwarka domów
> Dom
Wpisz swoje roczne dochody:
Wpisz liczbę sypialni:
Wpisz kod pocztowy:


40

Rozdział 1.

Sprawdza pole rocznych dochodów, by upewnić się, że została w nim wpisana liczba.

Reprezentuje wartość pola tekstowego służącego do podania kodu pocztowego.

Oblicza i wyświetla maksymalną cenę domu, gdy użytkownik kliknie przycisk Oblicz cenę.

Interaktywna WWW

Użyj znacznika



Nie ma

niemądrych pytań

P: A zatem wszystko, co zapisuję P: W takim razie, czy istnieją wewnątrz znaczników
Dom
Wpisz swoje roczne dochody:
Wpisz liczbę sypialni:
Wpisz kod pocztowy:


44

Rozdział 1.

Okienko dialogowe informujące o nieprawidłowo podanej liczbie.

alert

JavaScript / kod niestandardowy

Niestandardowy fragment kodu obliczający cenę domu.

calcPrice

JavaScript / kod niestandardowy

Przydziela w pamięci miejsce na przechowywanie informacji.

var

JavaScript / kod niestandardowy

Niestandardowy fragment kodu odnajdujący domy na podstawie pewnych kryteriów.

findHouses

JavaScript / kod niestandardowy

zipCode

JavaScript / kod niestandardowy

Miejsce w pamięci służące do przechowywania kodu pocztowego wpisanego przez użytkownika.

onblur

JavaScript / kod niestandardowy

Informuje, że użytkownik przeszedł do następnego pola formularza.

value

JavaScript / kod niestandardowy

Aktualna wartość pola formularza, w którym użytkownik podaje kod pocztowy.

onclick

JavaScript / kod niestandardowy

Informuje, że przycisk Szukaj domu został kliknięty.

Interaktywna WWW

Najlepszy wirtualny przyjaciel mężczyzny… potrzebuje TWOJEJ pomocy Ze względu na reputację specjalisty, jaką zyskałeś dzięki pisaniu stron HTML i arkuszy stylów CSS, zostałaś wezwany do gabinetu szefa, który chciał pokazać Ci swój najnowszy internetowy pomysł: iGłaz. Ten internetowy „zwierzaczek” wzbudzał duże zainteresowanie na wszystkich konferencjach i pokazach branży zabawkarskiej, niemniej jego testowi użytkownicy nie byli zbyt zadowoleni ze swego internetowego podopiecznego. Okazało się, że użytkownicy klikali swoje iGłazy, oczekując, że stanie się coś ciekawego… jednak szef tego nie przewidział. Zatem teraz to na Ciebie spadło zadanie zapewnienia iGłazowi interaktywności i zebrania zasłużonej chwały bądź klęska i niechlubny koniec.

Oto iGłaz w swojej dotychczasowej postaci… określanej jedynie przez HTML i CSS. A to oznacza brak jakiejkolwiek interakcji z użytkownikami.

Użytkownicy klikają swoje iGłazy, ale nic się nie dzieje.

Testowi użytkownicy iGłazu nękający dział wsparcia technicznego niezliczonymi telefonami.

On w ogóle nie reaguje na to, co robię!

Czy coś jest nie w porządku z moją przeglądarką?

Czy powiedziałem coś złego?

Daj mi jakiś znak!

Czy czujesz kliknięcia mojej myszki?

WYTĘŻ umysł

Jak sądzisz, jak powinna wyglądać interakcja iGłazu z użytkownikami? Co iGłaz powinien robić?

jesteś tutaj 

45

Planowanie interakcji

Zapewnianie interaktywności iGłazowi Twoje zadanie nie ogranicza się jednak wyłącznie do zapewniania interaktywności iGłazowi, realizacja tego zadania będzie bowiem wymagać od Ciebie także poznania języka JavaScript. Ale to nic… zanim się obejrzysz, iGłaz będzie się już z Tobą grzecznie witał. Oto, co masz zamiar zrobić w dalszej części tego rozdziału:

1

Stworzyć specjalną stronę WWW dla iGłazu.

zrobić. To potrafisz już

2 Dodać do niej wywołanie funkcji alert JavaScriptu, tak by iGłaz

witał użytkowników, którzy wyświetlą jego stronę.

3

Napisać kod, który „poprosi” użytkownika o podanie imienia, wyświetli spersonalizowane powitanie i zmodyfikuje iGłaz, tak by się uśmiechał.

W języku JavaScript funkcja alert to sposób wyświetlania prostych informacyjnych okienek dialogowych.

W tym przypadku łączysz czynność wykonywaną przez użytkownika, taką jak kliknięcie wirtualnego iGłazu…

4 Dodać procedurę obsługi zdarzeń, która sprawi, że kiedy

użytkownik kliknie iGłaz, zostanie wykonany kod z punktu 3.

5 Zyskać podziw szefa i stosowną premię.

46

Rozdział 1.

…z czynnością, którą sam określiłeś.

Interaktywna WWW

Utworzenie strony WWW iGłazu Trudno sobie wyobrazić prostszą stronę WWW niż ta, którą stworzysz dla iGłazu. Nie ociągaj się zatem i wpisz poniższy kod HTML w swoim ulubionym edytorze, a następnie zapisz w pliku o nazwie irock.html. Przykłady do książki, w tym także kody i rysunki używane na stronie iGłazu, możesz znaleźć na serwerze FTP wydawnictwa Helion (ftp://ftp.helion.pl/przyklady/hfjsc.zip).

Strona HTML iGłazu jest równie nużąca jak sam głaz… Nic dziwnego, że szef potrzebował Twojej pomocy.

iGłaz - Wirtualny Kamienny Ulubieniec
iGłaz
...



Nie zapomnij pobrać pliku rock.png ch z przykładów do książki dostępny na serwerze FTP wydawnictwa Helion.

...

irock.html

Test Zanim zabierzesz się do dalszej pracy, zapisz stronę WWW iGłazu i sprawdź w przeglądarce, jak wygląda. Upewnij się, że Twoja wersja wygląda tak samo jak nasza, bo już zaraz zaczniesz dodawać do niej interaktywność.

Nie ma

niemądrych pytań

P: Czy w znaczniku
umieszczony został kod CSS?

O: Jasne. Dobre, prawda? P: Sądziłem, że umieszczanie kodu CSS Już za kilka stron ten nudny głaz będzie się uśmiechał i rozmawiał z użytkownikami.

bezpośrednio w kodzie HTML jest kiepskim pomysłem. Dlaczego zatem tak zrobiono?

O

: Pewnie czytałeś książkę Head First HTML, CSS i XHTML. Edycja polska, prawda? Owszem, masz rację. Zazwyczaj znacznie lepiej jest umieszczać kod CSS w znaczniku

Na tę część strony składa się używany na niej kod CSS, odpowiadający za określenie wyglądu strony, w tym takich jego aspektów jak czcionki, kolory, a nawet układ treści.

To kod napisany w języku JavaScript, który obsługuje działanie strony i zapewnia jej interaktywność. Możesz go sobie wyobrazić jako tę część stro ny, która odpowiada za jej działanie.

Zagadnienia związane z separacją kodu można wyobrazić sobie w następujący sposób. Załóżmy, że Jasiek i Szymek znaleźli naprawdę świetny skrypt do zarządzania miejscami w sali kinowej i chcieliby go zastosować zamiast własnego. W takim przypadku musieliby przebudować aplikację w taki sposób, by korzystała z kodu nowego skryptu, jednak tym samym naraziliby się na ryzyko uszkodzenia struktury strony, gdyż ich kod JavaScript jest ściśle zintegrowany z kodem HTML. Byłoby zatem znacznie lepiej, gdyby kod HTML został odseparowany, a powiązanie kodu JavaScript i HTML występowało wyłącznie w kodzie JavaScript.

Separacja funkcjonalności od zawartości ułatwia tworzenie aplikacji internetowych oraz ich późniejsze utrzymanie.

WYTĘŻ umysł

Czy masz jakiś pomysł na zastosowanie funkcji w celu odseparowania funkcjonalności od zawartości w aplikacji Mandango?

jesteś tutaj  291

Dane = funkcje

Funkcje są zwykłymi danymi Abyś mógł efektywnie rozdzielić kod, musisz najpierw zrozumieć, w jaki sposób funkcje są powiązane ze zdarzeniami. Do tej pory kojarzenie to było realizowane przy użyciu atrybutów HTML. Istnieje jednak inny sposób, który przez wiele osób jest uważany za znacznie lepszy od mieszania kodu HTML i JavaScript. Jednak zastosowanie go wymaga zupełnie innego spojrzenia na funkcje. Choć to zaskakujące, jednak w rzeczywistości funkcje są zwyczajnymi danymi. Tak, to prawda. Cała tajemnica polega na tym, że ciało funkcji jest wartością, a jej nazwa — zmienną. Poniżej przedstawiliśmy sposób prezentacji funkcji, który znasz i do którego jesteś przyzwyczajony:

?

Ta funkcja jest tworzona w doskonale znany, standardowy sposób.

function showSeatStatus(seatNum) { alert("To miejsce jest " + getSeatStatus(seatNum) + "."); } Ten kod działa bardzo dobrze, jednak tę samą funkcję można utworzyć także w inny sposób:

var showSeatStatus

Nazwą funkcji jest w tym przypadku nazwa zmiennej.

= function(seatNum) {

alert("To miejsce jest " + getSeatStatus(seatNum) + "."); } ;

Wartością zmiennej jest ciało funk które jeśli zostanie wyrażone właś cji, w taki sposób, jest także określan nie e jako literał funkcyjny.

Powyższy przykład pokazuje, w jaki sposób można utworzyć funkcję, wykorzystując przy tym składnię charakterystyczną dla określania wartości zmiennych. Jak widać, używane są przy tym nawet te same elementy kodu: unikalny identyfikator (nazwa funkcji) oraz wartość (ciało funkcji). Kiedy ciało funkcji występuje samo, bez nazwy funkcji, nazywane jest literałem funkcyjnym. Ta niesamowita informacja dotycząca funkcji jest tak interesująca, gdyż pokazuje, że funkcjami można posługiwać się podobnie jak zmiennymi. Jak sądzisz, co robi przedstawiony poniżej fragment kodu?

var myShowSeatStatus = showSeatStatus; Zapisuje funkcję showSeatStatus() w zmiennej myShowSeatStatus.

292

Rozdział 6.

Funkcje

Wywołania i odwołania do funkcji Kiedy przypiszesz nazwę funkcji do innej zmiennej, udzielisz jej dostępu do ciała tej funkcji. Innymi słowy, będziesz mógł wywołać tę funkcję, tak jak to pokazaliśmy na poniższym przykładzie:

alert(myShowSeatStatus(23));

Wywołanie tej samej funkcji showSeatStatus() przy użyciu zmiennej myShowSeatStatus.

Końcowy efekty wywołania myShowSeatStatus() jest identyczny jak wywołania showSeatStatus(), gdyż w rzeczywistości obie funkcje odwołują się do tego samego kodu. Właśnie z tego powodu nazwa funkcji jest także nazywana odwołaniem lub referencją do funkcji.

showSeatStatus

function() { ...

myShowSeatStatus

}

Rozróżnienie pomiędzy odwoływaniem się do funkcji a jej wywoływaniem odbywa się na podstawie pary nawiasów (()) umieszczanych za jej nazwą. W przypadku odwoływania się do funkcji nawiasy te nie występują, natomiast w przypadku wywoływania — są obowiązkowe. Co więcej, w przypadku wywoływania funkcji w nawiasach często pojawiają się dodatkowe argumenty. Wywołanie funkcji myShowSeatStatus(), które w efekcie jest dokładnie tym samym co wywołanie funkcji showSeatStatus().

Tak naprawdę funkcja jest jedynie zmienną, której wartością jest odwołanie do ciała funkcji.

var myShowSeatStatus = showSeatStatus; myShowSeatStatus(23);

Zapisanie w zmiennej myShowSeatStatus odwołania do funkcji.

Przeanalizuj przedstawiony poniżej fragment kodu i zapisz liczbę, która zostanie wyświetlona w okienku dialogowym.

ćwiczenie

function doThis(num) { return num++; } function doThat(num) { return num--; } var x = doThis(11); var y = doThat; var z = doThat(x); x = y(z); y = x; alert(doThat(z – y));

jesteś tutaj  293

Rozwiązanie ćwiczenia

Przeanalizuj przedstawiony poniżej fragment kodu i zapisz liczbę, która zostanie wyświetlona w okienku dialogowym.

ćwiczenie Rozwiązanie

function doThis(num) { return num++; } function doThat(num) { return num--; } var x = doThis(11); var y = doThat; var z = doThat(x); x = y(z); y = x; alert(doThat(z – y));

x = 12 y = doThat z = doThat(12) = 11 x = doThat(11) = 10 y = 10 alert(doThat(11 – 10))

Nie ma

niemądrych pytań

P: Czy separacja zawartości jest

kodu nad tym samym projektem mogą pracować osoby o różnych specjalizacjach i doświadczeniach.

O: I tak, i nie. W przypadku

Na przykład projektant może pracować nad strukturą i prezentacją strony bez obaw, że jego zmiany spowodują pojawienie się błędów w funkcjonalnym kodzie JavaScript, którego projektant może nie rozumieć.

naprawdę tak ważna, by trzeba było zaprzątać sobie nią głowę?

niewielkich aplikacji umieszczanie w jednym pliku kodu HTML, JavaScript i CSS nie jest niczym niewłaściwym. Znaczenie i korzyści, jakie zapewnia separacja kodu, można znacznie łatwiej zauważyć dopiero w większych aplikacjach, w których tego kodu jest bardzo dużo. W takich dużych aplikacjach znacznie trudniej jest wypracować sobie bardziej ogólne pojęcie o działaniu kodu, zwłaszcza gdy różne rodzaje kodu są ze sobą wymieszane, a co za tym idzie — znacznie trudniej będzie modyfikować i pielęgnować takie aplikacje. Dzięki separacji kodu można czuć się bezpieczniej i mieć nadzieję, że zmiany funkcjonalne nie spowodują żadnych problemów w strukturze lub wyglądzie strony. Co więcej, dzięki separacji

294

Rozdział 6.

P: Skoro funkcje to tylko dane,

to w jaki sposób mogę odróżnić funkcję od zwyczajnej zmiennej?

O: Różnica pomiędzy funkcją

a „zwyczajną” zmienną sprowadza się do tego, w jakim celu i jak jej używasz. Dane powiązane z funkcją (czyli jej kod) można wykonywać. Jeśli masz zamiar wywołać funkcję, to informujesz o tym interpreter JavaScriptu, umieszczając za jej nazwą nawiasy oraz w razie potrzeby listę argumentów.

P: Jaki jest cel tworzenia odwołań do funkcji?

O: W odróżnieniu od normalnych

zmiennych, które przechowują powiązane z nimi dane jako wartości w pamięci, funkcje przechowują odwołania do swego kodu. A zatem wartością zmiennej funkcyjnej nie jest sam kod funkcji, lecz odwołanie do miejsca w pamięci, w którym ten kod się znajduje. Zatem w przypadku funkcji zmienna przypomina nieco adres pocztowy, który wskazuje, o jaki dom chodzi, lecz nie jest tym domem. Funkcje wykorzystują odwołania, a nie faktyczne wartości, gdyż rozwiązanie to jest bardziej efektywne niż tworzenie i przechowywanie wielu kopii kodu. A zatem przypisując funkcję procedurze obsługi zdarzeń (co niebawem) zrobisz, w rzeczywistości przypisujesz jedynie odwołanie do jej kodu, a nie sam kod.

Funkcje

No dobrze, może i wychodzi na to, że odwołania do funkcji są super, ale co one mają wspólnego z separacją zawartości od funkcjonalności?

*numer zwrotny dla funkcji Odwołania do funkcji są ściśle powiązane ze specjalnym sposobem wywoływania funkcji, który z kolei ma bardzo dużo wspólnego z separacją zawartości od funkcjonalności. Na przykład spotkałeś już poniższe wywołanie — było ono stosowane w kodzie aplikacji Mandango:

setSeat(i * seats[i].length + j; "select", "Twoje miejsce");

function setSeat(seatNum, status, description) { ... }

Jednak to nie jest jedyny możliwy sposób wywoływania funkcji. Inne funkcje, nazywane funkcjami zwrotnymi, mogą być wywoływane bez jakiegokolwiek jawnego działania ze strony programisty.

WYTĘŻ umysł

Jak sądzisz, czy takie funkcje zwrotne mogłyby się przydać w aplikacji Mandango? Jeśli tak, to w jaki sposób można by je wykorzystać?

jesteś tutaj  295

Funkcja normalna i zwrotna

Pogawędki przy kominku

Temat dzisiejszej pogawędki: Konfrontacja Funkcji normalnej z Funkcją zwrotną

Funkcja normalna:

A zatem to ty jesteś tą „panienką z okienka”, która nie przyjmuje wywołań lokalnych? Skąd to stanowisko?

Słucham… jak przeglądarka? To doprawdy egzotyczne podejście. Sądzę, że po prostu nie potrafisz się dogadać z tymi spośród nas, które w normalny sposób komunikują się z kodem skryptu.

Też coś… a czy kogoś to w ogóle obchodzi? Wcale! Kto się przejmuje tym, co dzieje się poza skryptem?

Funkcja zwrotna: Żadne tam stanowisko. Po prostu służę do innych celów. Podobnie jak przeglądarka WWW wolę, gdy wywołania przychodzą do mnie z odległych, egzotycznych miejsc.

Słuchaj… tu nie chodzi o to, która z nas jest lepsza lub gorsza. Wszystkie jesteśmy częściami kodu, ja po prostu zapewniam możliwość uzyskania dostępu do kodu przeróżnym outsiderom. Gdyby nie ja, nie miałabyś pojęcia o jakichkolwiek zdarzeniach zachodzących poza skryptem. Prawdę mówiąc — wszyscy. Pamiętaj, że celem tworzenia skryptów jest poprawienie funkcjonalności i atrakcyjności stron. Gdyby skrypt nie posiadał żadnych sposobów wykrywania zachodzących poza nim zdarzeń i reagowania na nie, to trudno by było mówić o poprawianiu czegokolwiek.

Wiesz… w tym przypadku może i masz trochę racji. Lubię wiedzieć, kiedy zostanie zakończone wczytywanie strony albo kiedy użytkownik coś kliknie czy wpisze. I twierdzisz, że gdyby nie ty, w ogóle bym nie wiedziała o takich zdarzeniach? Właśnie. Przeglądarka wywołuje mnie, a ja w wielu przypadkach wywołuję ciebie, ponieważ reagowanie na zdarzenia zewnętrzne często wymaga wykonania kilku funkcji. Cóż, miło usłyszeć, że w rzeczywistości wcale się tak bardzo nie różnimy. No właśnie. A zatem, jak sądzę, kiedyś się jeszcze spotkamy. Nie dzwoń do mnie, sama się odezwę. Ciekawe… Życzę powodzenia.

296

Rozdział 6.

Funkcje

Zdarzenia, funkcje zwrotne i atrybuty HTML Funkcje zwrotne, wywoływane przez przeglądarkę, a nie przez nasz kod, stosujemy już od dłuższego czasu. Najczęściej znajdują one zastosowanie podczas obsługi zdarzeń. Na przykład trudno byłoby wyobrazić sobie działanie aplikacji Mandango bez takich funkcji zwrotnych. W praktyce problem mieszania kodu HTML i JavaScript w bardzo dużym stopniu dotyczy właśnie funkcji obsługujących zdarzenia.

Przeglądarka wywołuje funkcję initSeats() po zakończeniu wczytywania strony.

onload! initSeats();

onclick! showSeatStatus(23);

Funkcje zwrotne są kojarzone ze zdarzeniami w kodzie HTML aplikacji Mandango: Atry



but onload języka HTML umo skojarzenie funkcji initSeats() ze żliwia zdarzeniem onload.

Atrybut onclick języka HTML umożliwia skojarzenie funkcji showSeatStatus() ze zdarzeniem onclick.

Ta technika kojarzenia funkcji obsługujących zdarzenia ze zdarzeniami przy wykorzystaniu atrybutów HTML działa doskonale, jednak ma pewną wadę — wymaga mieszania kodu HTML i JavaScript. Na szczęście odwołania do funkcji pozwalają uniknąć tego niepotrzebnego bałaganu i odseparować oba rodzaje kodu.

Czy mogę oddzwonić?

jesteś tutaj  297

Unikanie mieszania kodu przy użyciu odwołań do funkcji

Określanie procedur obsługi zdarzeń przy użyciu odwołań do funkcji Zamiast kojarzyć funkcje zwrotne z procedurami obsługi zdarzeń, używając w tym celu atrybutów HTML, można to zrobić bezpośrednio w kodzie HTML. Innymi słowy, w ogóle nie będziesz musiał ingerować w kod HTML — wystarczy określić funkcję zwrotną, używając odwołania do funkcji, a wszystko wyłącznie z poziomu kodu JavaScript.

window.onload = initSeats;

Za nazwą funkcji nie ma nawiasów, gdyż nie chcemy wywoływać tej funkcji — interesuje nas odwołanie do niej.

zostaje Odwołanie do funkcji initSeats() zapisane we właściwości onload.

Zdarzenie onload jest właściwością obiektu window.

Jak zatem widać, określenie procedury obsługi zdarzeń z poziomu kodu JavaScript polega na zapisaniu odwołania do funkcji w odpowiedniej właściwości odpowiedniego obiektu. W powyższym przykładzie przypisanie odwołania do funkcji we właściwości onload sprawi, że każde zgłoszenie zdarzenia onload spowoduje wywołanie funkcji initSeats(). Co więcej, wywołanie funkcji następuje automatycznie w odpowiedzi na zgłoszenie zdarzenia. Oto, jak wygląda ten proces:

onload!

window.onload(); Zdarzenie onload wywołuje procedurę obsługi zdarzeń za pośrednictwem właściwości window.onload…

initSeats();

…a ponieważ we właściwości tej zostało zapisane odwołanie do funkcji, zatem w celu obsługi zdarzenia onload zostanie wywołana funkcja initSeats().

Ogromną zaletą wykorzystania odwołań do funkcji i właściwości obiektów w celu określenia funkcji obsługujących zdarzenia jest możliwość całkowitego odseparowania kodu JavaScript od kodu HTML — nie ma już potrzeby umieszczania jakiegokolwiek kodu JavaScript w atrybutach znaczników HTML.



W końcu znacznik może pozostać znacznikiem , gdyż funkcja obsługująca zdarzenia onload została określona z poziomu kodu JavaScript. Musimy tylko zadbać o to, by kod określający funkcję obsługującą zdarzenia został wykonany możliwie jak najwcześniej; dlatego też zazwyczaj jest on umieszczany w sekcji nagłówka strony. Jest jednak pewien problem. Co zrobić, gdy do funkcji obsługującej zdarzenia musimy przekazać jakiś argument, konieczny do jej prawidłowego wykonania? Problem ten nie występuje w przypadku zdarzeń onload używanych w aplikacji Mandango, jednak prawidłowe obsłużenie zdarzenia onclick wymaga już przekazania argumentu — numeru klikniętego miejsca. Odwołania do funkcji nie umożliwiają przekazywania argumentów… a zatem musimy przyjrzeć się temu zagadnieniu dokładniej.

298

Rozdział 6.

Odwołania zapewniają wygodny sposób powiązania funkcji ze zdarzeniami, które te funkcje mają obsługiwać.

Funkcje

Literały funkcyjne spieszą z odsieczą W wyniku kliknięcia obrazka fotela w aplikacji Mandango ma zostać wywołana funkcja showSeatStatus(), a do niej przekazany argument — liczba określająca numer klikniętego miejsca. W tym przypadku zwyczajne przypisanie odwołania do funkcji nie zda egzaminu, gdyż nie pozwoli nam przekazać argumentu. Mamy zatem problem. Na szczęście istnieje pewne rozwiązanie. Polega ono na użyciu literału funkcyjnego do utworzenia odwołania do funkcji i wywołaniu funkcji showSeatStatus() z określonym argumentem wewnątrz tego literału.

onclick!

a fotela, aby Pobieramy obiekt obrazk właściwości o jeg uzyskać dostęp do onclick.

document.getElementById("seat23").onclick = function(evt) { showSeatStatus(23); }; Literał funkcyjny zawiera wywołanie metody showSeatStatus(), dzięki czemu możemy przekazać do niej argument.

Literał funkcyjny jest zapisywany we właściwości onclick jako odwołanie do funkcji.

Literał funkcyjny jest używany wyłącznie jako „pojemnik”, w którym zostanie umieszczone wywołanie funkcji showSeatStatus(); niemniej pełni on kluczową rolę, gdyż zapewnia możliwość przekazania do niej argumentu określającego numer klikniętego miejsca. Możesz wyobrazić sobie ten literał funkcyjny jako bezimienną funkcję obsługującą zdarzenia. Właśnie z tego powodu literały funkcyjne są także nazywane funkcjami anonimowymi. Powyższy kod pokazuje, że JavaScript udostępnia obiekt zdarzenia, który jest przekazywany do procedury obsługi zdarzeń, w tym przypadku pod postacią argumentu evt. Obiekt ten zawiera informacje charakterystyczne dla konkretnego typu zdarzenia. W tym przypadku nie interesują nas żadne dodatkowe informacje o zdarzeniu, zatem możemy zignorować argument evt i w ogóle go nie używać.

ćwiczenie

e Obiekt zdarzenia jest automatyczni ugi obsł y edur proc do y przekazywan jej zdarzeń jako pierwszy argument wywołania.

Literały funkcyjne pozwalają nam tworzyć anonimowe funkcje obsługujące zdarzenia.

Skojarz funkcję initSeats() z procedurą obsługi zdarzeń onload, używając do tego celu literału funkcyjnego, a nie odwołania do funkcji. ............................................................................................................................... ............................................................................................................................... ...............................................................................................................................

jesteś tutaj  299

Rozwiązanie ćwiczenia

Skojarz funkcję initSeats() z procedurą obsługi zdarzeń onload, używając do tego celu literału funkcyjnego, a nie odwołania do funkcji.

ćwiczenie window.onload = function(evt) { Rozwiązanie ............................................................................................................................... initSeats(); ............................................................................................................................... }; ............................................................................................................................... Funkcja initSeats() jest wywoływana wewnątrz literału funkcyjnego obsługującego zdarzenie onload.

Argument evt jest ignorowany, gdyż procedura obsługi zdarzeń onload nie potrzebuje żadnych informacji o zdarzeniu.

Czym jest kojarzenie? Wciąż pozostaje nam do rozwiązania jeden problem związany z kojarzeniem procedur obsługi zdarzeń przy wykorzystaniu literałów funkcyjnych. Wiemy już, że procedurę obsługi zdarzeń onload możemy określić w sekcji nagłówka strony WWW, w znaczniku

Opis sceny jest wyświetlany na stronie poprzez przypisanie zmiennej message do właściwości innerHTML.

Przygoda kreskowego ludzika

Proszę wybrać:


jesteś tutaj  367

Pytaj śmiało

Nie ma

niemądrych pytań

P: Czy za pomocą metody

P

getElementById() można uzyskać dostęp do każdego elementu strony?

: Czy właściwości innerHTML można używać do określania zawartości dowolnego elementu HTML?

O: Owszem, ale pod warunkiem że

O: Nie. Aby móc skorzystać z tej właściwości,

element ten ma określoną wartość atrybutu id oraz że wartość ta jest unikalna. Atrybut id ma kluczowe znaczenie podczas stosowania metody getElementById().

element musi pozwalać na umieszczanie wewnątrz niego jakiejś zawartości. Zatem w praktyce właściwość ta nadaje się do określania zawartości takich elementów jak div, span bądź p oraz wszelkich innych elementów pełniących rolę „pojemników”, w których umieszczana jest inna zawartość strony.

P: Co się dzieje z zawartością

elementu w momencie określenia jej przy użyciu właściwości innerHTML?

O: W takim przypadku dotychczasowa

zawartość elementu jest całkowicie usuwana. Oznacza to, że nie ma czegoś takiego jak dopisywanie nowych treści do zawartości elementu przy użyciu właściwości innerHTML. Oczywiście można uzyskać taki efekt poprzez zapisanie we właściwości innerHTML jej bieżącej wartości połączonej z nowy kodem. Oto przykład: elem.innerHTML += "To zdanie zostanie dopisane.”.

Nie tak szybko. Słyszałam, że właściwość innerHTML nie jest nawet ujęta w standardzie języka HTML. Czy to prawda?

Cóż, to faktycznie prawda… jednak czy standardy HTML są czymś, czym warto się przejmować? To prawda — właściwość innerHTML została początkowo stworzona przez firmę Microsoft i była dostępna wyłącznie w przeglądarce Internet Explorer. Jednak potem została dodana także do innych przeglądarek i powoli stała się nieoficjalnym standardem pozwalającym na szybkie i łatwe modyfikowanie zawartości elementów HTML. Jednak fakt pozostaje faktem — właściwość innerHTML do standardu nie należy. Można sądzić, że nie jest to wielki problem, jednak standard powinien zapewniać prawidłowe działanie stron i aplikacji internetowych w możliwie jak największej liczbie przeglądarek i platform systemowych. Poza tym trzeba wiedzieć, że istnieje także sposób modyfikowania zawartości stron całkowicie zgodny ze standardami — sposób znacznie bardziej elastyczny i dający większe możliwości, choć z drugiej strony nie tak prosty i szybki jak użycie właściwości innerHTML. Sposób ten wiąże się z zastosowaniem obiektowego modelu dokumentu DOM (ang. Document Object Model) — kolekcji obiektów zapewniających skryptom całkowitą i niczym nieograniczoną kontrolę nad zawartością i strukturą stron WWW.

368

Rozdział 8.

Modyfikacje stron WWW

Aby zobaczyć lasy i drzewa — obiektowy model dokumentu (DOM) DOM zapewnia skryptom możliwość dostępu do struktury oraz zawartości stron WWW i pozwala na manipulowanie nimi, co jest bardzo ważne, jeśli chcemy je dynamicznie modyfikować przy użyciu JavaScriptu. Spoglądając na stronę WWW tak, jak widzi ją DOM, okazuje się, że strona wraz ze wszystkimi jej elementami ma kształt drzewa. Liście tego drzewa są węzłami, a każdy z nich bezpośrednio odpowiada jednemu elementowi strony. Kiedy jeden węzeł drzewa jest umieszczony poniżej innego, mówimy, że jest jego potomkiem.

samym środku lasu. Stoisz sam w

No dobrze. Fakt, że to drzewko wygląda nieco dziwnie, jednak węzły swoim kształtem faktycstrony przypominają drzewo. znie

Wierzchołkiem każdego drzewa DOM jest węzeł document, umieszczony bezpośrednio nad węzłem elementu html.

Document



To wszystko są węzły.



html

DOM

DOM „widzi” body

stronę WWW

head

jako hierarchiczne drzewo węzłów. Odstępy otaczające na stronie znacznik

są interpretowane jako pusty tekst.

Wytłuszczone słowo "sam" jest umieszczone poniżej węzła znacznika .

""

"Stoisz"

p

strong

""

"w samym środku lasu."

"sam"

jesteś tutaj  369

Klasyfikacja węzłów drzewa DOM

Twoja strona jest kolekcją węzłów DOM

Węzły DOM są klasyfikowane na podstawie typu.

Każdy węzeł należący do drzewa DOM jest klasyfikowany według swojego typu. Podstawowe typy węzłów odpowiadają strukturalnym fragmentom strony; należą do nich przede wszystkim węzły elementów oraz węzły tekstowe.

DOCUMENT

ELEMENT

żonym Węzeł dokumentu — jest najwyżej poło sam tuje ezen repr M, węzłem drzewa DO dnio dokument i jest umieszczony bezpośre . html entu powyżej węzła elem

Węzeł elementu — odpowiada bezpośrednio znacznikowi w kodzie HTML.

ATTRIBUTE

TEXT

but Węzeł atrybutu — reprezentuje atry ęp dost o nieg elementu, można uzyskać do jest nie ; entu za pośrednictwem węzła elem M. dostępny bezpośrednio w drzewie DO

Węzeł tekstowy — reprezentuje tekstową zawartość elementu, zawsze jest umieszczany bezpośrednio poniżej węzła elementu.

document

Przypisanie typów do poszczególnych węzłów drzewa DOM pomaga zrozumieć, w jaki sposób DOM „widzi” poszczególne elementy strony. Szczególnie ciekawe są węzły tekstowe (TEXT), które zawsze są umieszczane bezpośrednio poniżej węzła elementu (ELEMENT) i stanowią fragment (lub całość) jego zawartości.

Document

ELEMENT

html



Choć atrybuty mają sw typ węzłów i można do ój własny dostęp za pośrednictwe nich uzyskać jednak nie są dostępne m DOM, to w drzewie węzłów strony.

Stoisz sam w samym środku lasu.



ELEMENT body

ELEMENT text



""

p

""

text

"Stoisz

strong

ELEMENT

text

"sam"

Rozdział 8.

head

text

text

370

ELEMENT

"w samym środku lasu." Choć w naszym przypadku element head jest pusty, to jednak w przeważającej większości stron będzie on miał jakieś węzły potomne.

Modyfikacje stron WWW

Zaostrz ołówek Uzupełnij reprezentację drzewa DOM strony Przygoda kreskowego ludzika. W tym celu zapisz nazwy poszczególnych węzłów tego drzewa oraz podaj typ każdego z węzłów.

...
Przygoda kreskowego ludzika

Proszę wybrać:


Document



html

head

...

jesteś tutaj  371

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie

Uzupełnij reprezentację drzewa DOM strony Przygoda kreskowego ludzika. W tym celu zapisz nazwy poszczególnych węzłów tego drzewa oraz podaj typ każdego z węzłów.

...
Przygoda kreskowego ludzika

Proszę wybrać:

document



Document



ELEMENT



html

Odstępy przed i za elementem są traktowane jak puste elementy tekstowe.

body ELEMENT

head

ELEMENT ELEMENT

div

"" text

img

""

br

div

br

text

text

ELEMENT

372

""

Rozdział 8.

ELEMENT

...

"" text

"Proszę wybrać:"

text

input

""

ELEMENT

text

input

""

ELEMENT text

Modyfikacje stron WWW

Poruszanie się po drzewie DOM przy użyciu właściwości Większość operacji na DOM rozpoczyna się od obiektu document, stanowiącego najwyższy węzeł drzewa DOM. To właśnie ten obiekt udostępnia tak użyteczne metody jak getElementById(), getElementsByTagName() oraz wiele równie przydatnych właściwości. Wiele właściwości obiektu document jest dostępnych w każdym węźle drzewa DOM. Niektóre z nich pozwalają nawet na przechodzenie do innych węzłów, a to oznacza, że można ich używać do poruszania się po drzewie.

nodeValue

nodeType

Wartość zapisana w węźle, dostępna wyłącznie w węzłach tekstowych oraz węzłach atrybutów (a nie w węzłach elementów).

Typ węzła (węzeł dokumentu, węzeł tekstowy itd.) wyrażony jako liczba.

childNodes

firstChild

Tablica zawierająca wszystkie węzły potomne umieszczone w drzewie DOM bezpośrednio poniżej danego węzła i zapisane w kolejności, w jakiej występują w kodzie HTML.

Pierwszy węzeł potomny umieszczony bezpośrednio poniżej danego.

Właściwości węzłów można wykorzystać do poruszania się po drzewie DOM.

lastChild Ostatni węzeł potomny umieszczony bezpośrednio poniżej danego.

Powyższe właściwości mają kluczowe znaczenie dla poruszania się po drzewie DOM i dostępu do danych poszczególnych węzłów. Możesz na przykład używać właściwości węzłów wraz z metodą getElementById(), która zapewnia szybki dostęp do deValue Właściwość nobranie wybranego węzła: po na a al zw po tości węzła. kstowej zawar

alert(document.getElementById("scenetext").nodeValue);

Właściwość nodeValue zawsze zawiera sam tekst, bez żadnego dodatkowego formatowania.

te

Opis sceny w Przygodzie kreskowego ludzika jest początkowo pusty.

No dobrze, może to nie jest najlepszy przykład, zważywszy na to, że tekst opisujący stronę w Przygodzie kreskowego ludzika jest początkowo pusty. Jednak później, wraz z rozwojem akcji, opis ten na pewno zostanie określony, a w takim przypadku powyższy przykład da znacznie lepsze rezultaty.

ćwiczenie

Zamieszczony poniżej fragment kodu odwołuje się do jednego z węzłów przedstawionych na stronie 372. Uważnie przestudiuj ten kod i zakreśl węzeł, do którego on się odwołuje. document.getElementsByTagName("body")[0].childNodes[1].lastChild

jesteś tutaj  373

Rozwiązanie ćwiczenia

ćwiczenie

Zamieszczony poniżej fragment kodu odwołuje się do jednego z węzłów przedstawionych na stronie 372. Uważnie przestudiuj ten kod i zakreśl węzeł, do którego on się odwołuje.

document.getElementsByTagName("body")[0].childNodes[1].lastChild Istnieje tylko jeden znacznik , zatem odwołując się do niego, musimy podać, że jest on pierwszym elementem tablicy zwracanej przez wywołanie meto dy getElementsByTagName().

Drugim węzłem potomnym elementu body jest element div.

““

““

img

br

““

div

Metoda getElementById() zwraca ny jeden element posiadający poda identyfikator.

br

niemądrych pytań

P: Jaka jest różnica w działaniu metod

getElementById() oraz getElementsByTagName() z punktu widzenia drzewa DOM? Dlaczego mógłbym chcieć używać pierwszej bądź drugiej z nich?

O: Obie te metody zapewniają dostęp do elementów drzewa DOM, choć każda prezentuje inne podejście do tego zagadnienia. Wybór jednej z nich sprowadza się do odpowiedzi na pytanie, co chcemy zrobić: czy pobrać konkretny element, czy też grupę podobnych elementów. Jeśli interesuje nas jeden element, to niezastąpiona będzie metoda getElementById() — jeśli tyko znamy identyfikator elementu, to pobranie węzła DOM nie przysporzy nam najmniejszego problemu.

374

Rozdział 8.

div

head

...

““

input

Ostatnim węzłem potomnym głównego elementu div jest pusty węzeł tekstowy.

html

body

“Proszę wybrać:“

Nie ma

Document

““

input

““

Metoda getElementsByTagName() zwraca wszystkie węzły reprezentujące konkretny znacznik , taki jak lub .

Jeśli jednak interesuje nas grupa węzłów, to znacznie lepszym rozwiązaniem będzie skorzystanie z metody getElementsByTagName(). Gdybyśmy na przykład chcieli ukryć wszystkie obrazki na stronie, używając do tego celu skryptu, to moglibyśmy wywołać metodę getElementsByTagName(), przekazując w jej wywołaniu łańcuch znaków "img”; w efekcie uzyskalibyśmy tablicę zawierającą węzły wszystkich obrazków znajdujących się na stronie. Teraz wystarczyłoby skorzystać z właściwości CSS visibility każdego z węzłów, by ukryć obrazki na stronie. Ups… trochę się pospieszyliśmy… do zagadnień związanych z DOM i CSS wrócimy jeszcze w dalszej części rozdziału. Teraz jednak rozumiesz, że choć metoda getElementsByTagName() nie jest tak popularna jak getElementById(), to jednak w pewnych sytuacjach także jest przydatna.

Modyfikacje stron WWW

A zatem właściwości węzłów pozwalają na poruszanie się po kodzie HTML i dostęp do zawartości strony… ale czy można ich używać do modyfikowania tej zawartości?

Właściwości DOM pozwalają na modyfikację zawartości strony WWW przy jednoczesnym zachowaniu zgodności ze standardami. Ponieważ DOM traktuje całą zawartość dokumentu HTML jako grupę węzłów, zatem modyfikowanie strony oznacza modyfikowanie tworzących ją węzłów. W przypadku zawartości tekstowej tekst umieszczony w takich elementach jak div, span bądź p zawsze staje się wartością węzła tekstowego, będącego bezpośrednim potomkiem węzła elementu. Jeśli tekst ten jest umieszczony w jednym węźle tekstowym i nie zawiera żadnych dodatkowych elementów HTML, to węzeł tekstowy będzie pierwszym potomkiem węzła elementu. document.getElementById("opowiesc").firstChild.nodeValue

p



ELEMENT

Ależ nie jesteś sam.



text "Ależ nie jesteś sam."

Ależ nie jesteś sam!

WYTĘŻ

umysł

Jak sądzisz, w jaki sposób można zmienić tekst węzła DOM?

jesteś tutaj  375

Problematyczne potomstwo

Modyfikowanie węzła tekstowego przy wykorzystaniu DOM Jeśli możesz bezpiecznie założyć, że węzeł ma tylko jednego potomka, w którym jest umieszczona cała jego zawartość węzła, to będziesz mógł określić jego zawartość, zapisując nowy łańcuch znaków we właściwości nodeValue. To rozwiązanie działa bardzo dobrze, ale pod warunkiem że dany węzeł ma tylko jeden potomny węzeł tekstowy. document.getElementById("opowiesc").firstChild.nodeValue = "No dobra, może jesteś sam."; p Nowy tekst zastępuje dotychczasową zawartość potomnego węzła tekstowego.

ELEMENT

text "No dobra, może jesteś sam."

Jednak życie nie zawsze jest tak proste. Co w sytuacji, gdy węzeł ma więcej niż jednego potomka, jak na poniższym przykładzie:



ELEMENT

p

Ależ nie jesteś sam.

text

Ten akapit jest podzie na kilka węzłów potomlony nych.

"Ależ"

Zmiana zawartości pierwszego potomka nie wystarczy, by zmienić całą zawartość akapitu.

Jeśli zmienimy tekst umieszczony wyłącznie w pierwszym potomku, to zawartość pozostałych nie ulegnie zmianie, a my w rezultacie uzyskamy dziwne wyniki, takie jak te przedstawione poniżej:

ELEMENT strong

text "jesteś sam."

text "nie"

Ponieważ w tekście akapitu jest umieszczony inny znacznik HTML, zatem jego zawartość jest umieszczona w kilku węzłach potomnych.

document.getElementById("opowiesc").firstChild.nodeValue = "No dobra, może jesteś sam.";

Została zmieniona zawartość wyłącznie pierwszego potomka, a w reszcie pozostała oryginaln a zawartość, co w efekcie dało dziwaczne wyniki.

376

Rozdział 8.

No dobra, może jesteś sam nie jesteś sam.

Modyfikacje stron WWW

Trzy (bezpieczne) kroki zmieniania zawartości węzłów tekstowych Problem związany ze zmianą zawartości węzła poprzez zmianę zawartości wyłącznie pierwszego węzła potomnego polega na tym, że w ogóle nie uwzględnia on możliwości istnienia innych węzłów potomnych. A zatem aby zmienić zawartość węzła, powinniśmy usunąć wszystkie jego węzły potomne, a następnie dodać nowy z inną zawartością. 1

Usuwamy wszystkie węzły potomne.

2

Tworzymy nowy węzeł tekstowy z nową zawartością.

3

Dodajemy nowy węzeł będący potomkiem naszego wybranego węzła.

Powyższe czynności możemy wykonać, używając trzech metod DOM: 1

removeChild() Ta metoda usuwa z węzła węzeł potomny przekazany jako argument wywołania. 2

createTextNode() Ta metoda tworzy nowy węzeł tekstowy na podstawie przekazanego łańcucha znaków.

3

appendChild() Z kolei ta metoda dodaje do węzła nowy węzeł potomny przekazany jako argument wywołania. Węzeł ten dodawany jest na końcu kolekcji węzłów potomnych.

A zatem aby zastąpić cały tekst w naszym ostatnim przykładzie, musimy wykonać trzy opisane powyżej czynności — najpierw usunąć wszystkie węzły potomne akapitu, następnie stworzyć nowy węzeł tekstowy, a w końcu dodać nowy węzeł tekstowy do akapitu. var node = document.getElementById("opowiesc"); while (node.firstChild) 1 node.removeChild(node.firstChild);

eł) Najpierw pobieramy element (węz na podstawie jego identyfikatora. Następnie usuwamy pierwszego potomka węzła i powtarzamy tę czynność do momentu usunięcia wszystkich potomków.

node.appendChild(document.createTextNode("No dobra, może jesteś sam."); 3 Po usunięciu wszystkich potomków do węzła rodzica dodajemy nowy węzeł tekstowy.

2 Tworzymy nowy węzeł tekstowy.

No dobra, może jesteś sam. jesteś tutaj  377

DOM bez tajemnic

Elementy konstrukcyjne DOM Temat dzisiejszego wywiadu:

Węzeł opisuje mądrość drzew DOM Head First: Powiedziano mi, że jesteś najmniejszym

elementem przechowującym zawartość w drzewach DOM, czymś w rodzaju atomu HTML-owej zawartości. Czy to prawda?

Węzeł: Nie mam wyrobionego zdania odnośnie swojej atomowości, jednak bez wątpienia reprezentuję odrębny, niezależny fragment informacji umieszczony w drzewie DOM. Wyobraź sobie, że drzewo DOM dzieli całą stronę na maleńkie, niemal bitowej wielkości fragmenty informacji. To właśnie ja stanowię te elementy. Head First: Ale dlaczego to ma jakiekolwiek znaczenie? Chodzi mi o to, czy możliwość dzielenia strony na takie malutkie fragmenty ma jakieś znaczenie?

Węzeł: Ma znaczenie wyłącznie wtedy, gdy zależy ci na dostępie do wybranych fragmentów strony i zmodyfikowaniu ich. Dla wielu skryptów możliwość ta jest bardzo ważna i właśnie w takich przypadkach DOM ma duże znaczenie. Jednak tak naprawdę to ma to znaczenie dlatego, że możliwość podzielenia strony na takie malutkie elementy daje ogromną potęgę i władzę nad jej postacią i zawartością. Head First: A czy nie ma ryzyka, że coś zgubisz podczas

dzielenia strony? Zdecydowanie zbyt często, gdy coś rozkręcamy, zostają nam jakieś części, a potem okazuje się, że to uszkodziliśmy.

Węzeł: Nie, w tym przypadku to nie stanowi problemu, gdyż podczas odwoływania się do strony jako drzewa węzłów wcale nie musimy pobierać wszystkich jego elementów. DOM zapewnia „drzewiasty” charakter strony bez względu na to, czy planujesz wprowadzać w niej jakieś zmiany, czy nie. Head First: No to super. Ale zakładając, że chcielibyśmy coś usunąć ze strony, to kiedy ty wkraczasz do akcji? Czy w ogóle bierzesz w tym jakiś udział?

Węzeł: Tak. Jednak moje możliwości nie ograniczają się

do usuwania — nic nie stoi na przeszkodzie, bym dodawał także dane do strony.

Head First: Kurczę, to naprawdę niesamowite. Jak można coś takiego zrobić?

378

Rozdział 8.

Węzeł: No cóż… pamiętaj, że każda, nawet najmniejsza

informacja jest odwzorowywana w postaci drzewa węzłów. A zatem za moją pomocą jesteś w stanie dostać się w dowolne miejsce strony i pobrać znajdujące się tam dane. Mogę ci także pomóc w utworzeniu całkowicie nowego elementu zawartości strony oraz w dodaniu go do drzewa. DOM jest naprawdę bardzo elastyczny.

Head First: Fajnie. Ale męczy mnie jeszcze jedna rzecz, której do końca nie rozumiem. Jaki jest twój związek z elementami? Czy tak naprawdę ty i elementy to ta sama osoba?

Węzeł: Cóż, właściwie tak. Jednak ja idę o krok dalej niż

elementy. Pamiętasz zapewne, że element to tylko inny sposób pojmowania znacznika, takiego jak lub
. Każdy element na stronie jest reprezentowany przez węzeł w drzewie dokumentu, a zatem z tego punktu widzenia ja i element jesteśmy tym samym. Jednak ja mogę także reprezentować zawartość umieszczoną wewnątrz elementu. A zatem tekst wewnątrz elementu
także ma swój własny węzeł umieszczony w drzewie tuż poniżej węzła elementu div.

Head First: Hm… trudno to wszystko zrozumieć. A w jaki sposób wyjaśnisz różnicę pomiędzy elementem a jego zawartością?

Węzeł: Cóż, przede wszystkim zawartość elementu lub

węzła zawsze staje się w drzewie DOM jego węzłem potomnym. A poza tym wszystkie węzły można rozróżniać na podstawie typu: elementy zawsze stają się węzłami typu ELEMENT, natomiast ich zawartość — węzłami typu TEXT.

Head First: Jeśli zatem chcę pobrać zawartość elementu, to muszę szukać węzłów typu TEXT?

Węzeł: Mógłbyś tak zrobić, musisz jednak pamiętać, że

wartością właściwości nodeType jest liczba reprezentująca konkretny typ węzła. Na przykład dla węzłów typu TEXT właściwość ta przyjmuje wartość 3, natomiast dla typu ELEMENT — wartość 1. Jednak nawet to nie jest konieczne, gdyż w praktyce musisz jedynie odwołać się do potomków węzła elementu i pobrać ich zawartość.

Head First: Rozumiem. Dziękuję za poświęcony nam czas i za przedstawienie cudów drzewa DOM.

Węzeł: Proszę bardzo. I jeśli kiedykolwiek poczujesz ochotę, by popiłować drzewo, to nie zapomnij, że chętnie ci pomogę!

Modyfikacje stron WWW

Nie ma

niemądrych pytań

P: Wciąż nie do końca rozumiem, czym są węzły potomne i jak są one organizowane. Na przykład w jaki sposób działa właściwość childNodes?

O: Kiedy pewien węzeł zawiera dane, jest

pElem.childNodes[2]

Element p ma trzy węzły potomne.

p

strong

"Ależ"

"jesteś sam."

on uważany za rodzica, natomiast jego zawartość jest umieszczana w drzewie "nie" DOM jako jego węzły podrzędne. Jeśli Element strong w zawartości węzła znajdują się jakiekolwiek pEle ma jeden węzeł m.childNodes[0] znaczniki, to zostanie ona podzielona na potomny. większą liczbę węzłów potomnych. Węzły potomne są umieszczane we właściwości : Wyjaśnij mi, w jaki sposób childNodes, będącej tablicą obiektów, działa warunek umieszczony a ich kolejność w tej tablicy odpowiada w pętli while w kodzie, który kolejności w kodzie HTML. A zatem pierwszy usuwa węzły podrzędne? węzeł potomny można pobrać, używając wyrażenia childNodes[0]. : Dla przypomnienia poniżej zamieszczam Tablicę tę można także przejrzeć wewnątrz kod warunku tej pętli: pętli, uzyskując w ten sposób dostęp do while (node.firstChild) wszystkich węzłów potomnych.

P O

Warunek ten sprawdza, czy węzeł node zawiera pierwszy węzeł potomny. Jeśli ten pierwszy węzeł potomny istnieje, to wyrażenie umieszczone w warunku przyjmie wartość true, a pętla wykona kolejną iterację. Jeśli jednak węzeł nie ma pierwszego potomka, to oznacza to, że w ogóle nie ma żadnych potomków. W takim przypadku wyrażenie node.firstChild zwróci wartość null, która — w kontekście warunku pętli while — zostanie automatycznie skonwertowana na wartość false. A zatem tak naprawdę pętla while sprawdza, czy właściwość zawierająca odwołanie do pierwszego węzła potomnego ma wartość null, co będzie wyraźnym sygnałem, że węzeł nie ma żadnych potomków.

Magnesiki z JavaScriptem W nowej, zgodnej ze standardami DOM wersji Przygody kreskowego ludzika brakuje kilku ważnych fragmentów kodu. Skorzystaj z magnesików umieszczonych u dołu strony, by uzupełnić kod, który modyfikuje zawartość węzła tekstowego zawierającego opis sceny. Każdego z magnesików możesz użyć więcej niż jeden raz.

// Aktualizacja tekstowego opisu sceny = document.getElementById(" var

");

)

while (

);

(

));

(document.createTextNode(

firstChild removeChild

appendChild sceneText

scenetext

message

jesteś tutaj  379

Magnesiki z JavaScriptem — Rozwiązanie

Magnesiki z JavaScriptem W nowej, zgodnej ze standardami DOM wersji Przygody kreskowego ludzika brakuje kilku ważnych fragmentów kodu. Skorzystaj z magnesików umieszczonych u dołu strony, by uzupełnić kod, który modyfikuje zawartość węzła tekstowego zawierającego opis sceny. Każdego z magnesików możesz użyć więcej niż jeden raz. Pętla działa dopóty, dopóki węzeł elementu zawierającego opis sceny ma jakieś węzły potomne.

Pętla będzie działać dopóty, dopóki będzie istnieć pierwszy węzeł potomny.

W pierwszej kolejności pobieramy element zawierający tekstowy opis sceny; używamy przy tym jego identyfikatora.

// Aktualizacja tekstowego opisu sceny sceneText = document.getElementById(" scenetext "); var ) firstChild while ( sceneText );ld firstChi sceneText ( removeChild sceneText sceneText

nt.createTextNode( appendChild (docume

Cyklicznie usuwamy kolejne pierwsze węzły potomne, aż nie zostanie żaden z nich.

Kiedy element opisu nie będzie już zawierać żadnych potomków, uzyskamy pewność, że dodanie nowego węzła tekstowego spowoduje zastąpienie całej jego zawartości.

message

Komunikat musi zawierać wyłącznie sam tekst, bez jakichkolwiek znaczników HTML czy formatowania.

));

ich węzłów iu wszystk Po usunięc tworzymy nowy węzeł a potomnych dodajemy go do drzew tekstowy i jako potomka węzła dokumentu go opis sceny. zawierające

Kluczowe zagadnienia 

Właściwość innerHTML co prawda nie należy do żadnego standardu języka HTML, jednak zapewnia szybki i łatwy dostęp do całej zawartości węzła.



DOM, czyli Obiektowy Model Dokumentu, udostępnia standardowe mechanizmy pozwalające na uzyskanie dostępu do zawartości stron WWW i zmodyfikowanie ich.

380

Rozdział 8.





DOM postrzega stronę WWW jako hierarchiczne drzewo powiązanych ze sobą węzłów. Modyfikacja zawartości strony przy wykorzystaniu mechanizmów zapewnianych przez DOM, stanowiąca alternatywę dla stosowania właściwości innerHTML, wymaga wykonania kilku czynności: usunięcia węzłów potomnych elementu, utworzenia nowego węzła tekstowego z nową zawartością i dodania tego węzła jako potomka elementu.

Modyfikacje stron WWW

Przygoda standaryzowana Chłopie, ależ to musi być jazda! Jednym z symboli wszystkich wypraw i przygód jest zgodność ze standardami… a może i nie. Jednak bez wątpienia mogłoby tak być, zwłaszcza jeśli będziemy mówić o nowoczesnych aplikacjach internetowych. Co ważniejsze jednak, spójrz na dramatyczne zmiany, jakie spowodowało dynamiczne modyfikowanie tekstu opisu sceny…

Chwileczkę, ale tekst opisu sceny wygląda dokładnie tak samo jak wcześniej!

Skorzystanie z DOM jest standardowym sposobem manipulowania kodem HTML strony, co więcej, zapewnia Hm. No dobrze, zatem może strona nie wygląda inaczej, jednak dzięki zastosowaniu DOM wykorzystane na niej skrypty są zgodne z najnowszymi internetowymi standardami. Nie wszystko, co w kodzie JavaScript jest wspaniałe i godne polecania, można zauważyć i docenić na pierwszy rzut oka. W naszym przypadku głęboka satysfakcja z faktu stworzenia rozwiązania w pełni zgodnego ze standardami może się pojawić dopiero po przeanalizowaniu nowego kodu strony Przygody kreskowego ludzika.

znacznie większe możliwości i kontrolę niż korzystanie z właściwości innerHTML. jesteś tutaj  381

Ulepszanie wybieranych opcji

W poszukiwaniu lepszych opcji A zatem ostatnio już dwukrotnie zmienialiśmy sposób podmieniania tekstu opisującego scenę, jednak przyciski, które klika użytkownik, określając, co chce zrobić, wciąż pozostają tajemnicze. Oczywiście, na pewno można coś zrobić, żeby elementy sterujące, służące do poruszania się po Przygodzie..., były bardziej atrakcyjne i intuicyjne.

Przyciski z cyframi po prostu nie pasują — nie przekazują ji użytkownikowi żadnych informac ąć. na temat decyzji, jaką musi podj

Wiem, że przyciski spełniają swoje zadanie, jednak wyglądają gorzej niż nieatrakcyjnie. Przyciski powinny przekazywać użytkownikowi jakieś przydatne i znaczące informacje.

Całkiem rozsądną modyfikacją, jaką można by wprowadzić, jest zmiana przycisków, tak by odpowiadały decyzjom, które pozwalają podjąć. Na przykład na przycisku można by wyświetlać tekst, który dokładnie wyjaśniałby, co spowoduje jego kliknięcie. Oto przykład:

Znacznie lepiej! Teraz przyciski mogą odegrać jakąś rolę podczas podejmowania decyzji.

Zastanów się nad tym — tak naprawdę nie ma żadnego powodu, który zmuszałby nas do zastosowania przycisków znanych z formularzy. Dowolny element HTML zawierający tekst byłby w tym przypadku równie dobry. Wystarczyłoby określić odpowiednie style CSS, by zmodyfikować ich wygląd i upodobnić je nieco do typowych elementów sterujących.

WYTĘŻ umysł

W jaki sposób można by zaimplementować przyciski sterujące na stronie Przygody kreskowego ludzika, tak by prezentowały teksty zależne od aktualnie wybranej sceny?

382

Rozdział 8.

Modyfikacje stron WWW

Projektujemy większe i lepsze opcje Ponieważ nowe i poprawione opcje decyzyjne zastosowane na stronie Przygody kreskowego ludzika są zwyczajnymi elementami HTML zawierającymi pewne teksty, ich zawartość będziemy mogli dynamicznie modyfikować w każdej ze scen, używając w tym celu DOM. A to oznacza, że w każdej ze scen trzeba będzie określać nie tylko opis, lecz także teksty obu opcji. Oznacza to także, że w funkcji changeScene() będą się musiały pojawić dwie nowe zmienne do przechowywania tekstów wyświetlanych na obu opcjach; nadamy im nazwy decision1 oraz decision2.

decision 1

decision 2

Poniżej pokazaliśmy, w jaki sposób należałoby zmieniać komunikaty tekstowe w scenie nr 1 podczas przechodzenia do sceny nr 3: curScene = 3; message = "Stoisz na moście nad spokojnym strumieniem."; decision1

= "Przejdź przez most";

decision2

= "Zajrzyj do strumienia";

2 są Zmienne decision1 oraz decision tów używane do przechowywania teks opcji decyzyjnych dla aktualnie wybranej sceny.

Zaostrz ołówek Zastosowanie nowych opcji decyzyjnych na stronie Przygody kreskowego ludzika wymaga wprowadzenia zmian w kodzie HTML strony. Napisz kod nowych elementów tekstowych, które zastąpią stosowane wcześniej elementy . Podpowiedź: klasa CSS określająca postać nowych opcji decyzyjnych nosi nazwę decision, a pierwsza opcja początkowo powinna zawierać napis Rozpocznij grę.

cene(1)" /> "changeS onclick= " value="2 sion2" "changeS onclick= " value="2 sion2" id="deci tton" Rozpoc ........ znij grę ........ ........cene(1)" ........ ........ ........ ........

......................

Zmiana zawartości węzła tekstowego wydaje się być operacją, którą z powodzeniem można zaimplementować w postaci funkcji.

Rozważania nad zastępowaniem węzłów tekstowych Teraz jedyną rzeczą, jakiej brakuje w nowej wersji Przygody kreskowego ludzika, jest kod, który będzie dynamicznie określać teksty wyświetlane w obu elementach span. Kod ten będzie działał dokładnie tak samo jak napisany przez nas wcześniej kod zmieniający tekstowy opis sceny, który wykorzystywał możliwości DOM. Prawdę mówiąc, stwarza to pewien problem, gdyż okazuje się, że będziemy musieli wykonywać dokładnie tę samą operację na trzech różnych elementach: na opisie sceny oraz dwóch opcjach decyzyjnych…

384

Rozdział 8.

Modyfikacje stron WWW

Funkcja zastępująca tekst w węźle Funkcja ogólnego zastosowania potrafiąca zastąpić zawartość węzła tekstowego na pewno będzie wygodnym narzędziem i to nie tylko w Przygodzie kreskowego ludzika. Funkcja tego rodzaju będzie działać niemal tak samo jak kod zastępujący tekst, który przedstawiliśmy we wcześniejszej części rozdziału, z tym że węzeł, na jakim będzie ona operować, zostanie określony na podstawie argumentu wywołania.

function replaceNodeText(id, newText) { ...

Identyfikator węzła, którego zawartość ma być zastąpiona.

Tekst, który ma się stać nową zawartością węzła.

}

Nasza niestandardowa funkcja replaceNodeText() wymaga przekazania dwóch argumentów: identyfikatora węzła, którego zawartość należy zastąpić, oraz łańcucha znaków stanowiącego tę nową zawartość. Funkcji tej można będzie używać do zmiany zawartości dowolnego elementu na stronie, w którym można umieszczać tekst. W przypadku naszej aplikacji Przygody kreskowego ludzika funkcji tej będziemy używać do dynamicznej zmiany opisu sceny oraz napisów wyświetlanych na dwóch przyciskach decyzyjnych… Oczywiście, zanim to zrobimy, Ty będziesz musiał ją napisać. Zamiast powielać ten sam kod trzy razy, teraz zastosujemy trzy wywołania jednej funkcji.

W tym wywołaniu określamy nowy opis sceny. replaceNodeText("scenetext", message);

replaceNodeText("decision1", decision1); replaceNodeText("decision2", decision2);

Tu zmieniamy teksty wyświetlane na przyciskach decyzyjnych.

Zaostrz ołówek Napisz kod funkcji replaceNoteText(), której zadaniem będzie zmiana tekstu umieszczonego w węźle o podanym identyfikatorze. Nie zapomnij o tym, że funkcja musi mieć dwa argumenty: id oraz newText.

.................................................................................................................................................................... .................................................................................................................................................................... .................................................................................................................................................................... .................................................................................................................................................................... ....................................................................................................................................................................

jesteś tutaj  385

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie Pobieramy element na podstawie jego identyfikatora. Usuwamy z węzła elementu wszystkie węzły potomne.

Na podstawie tekstu przekazanego do funkcji tworzymy nowy węzeł tekstowy, który zostanie dodany jako potomek wskazanego elementu.

Napisz kod funkcji replaceNoteText(), której zadaniem będzie zmiana tekstu umieszczonego w węźle o podanym identyfikatorze. Nie zapomnij o tym, że funkcja musi mieć dwa argumenty: id oraz newText.

function replaceNodeText(id, newText) { .................................................................................................................................................... var node = document.getElementById(id); .................................................................................................................................................... while (node.firstChild) .................................................................................................................................................... node.removeChild(node.firstChild); .................................................................................................................................................... node.appendChild(document.createTextNode(newText)); .................................................................................................................................................... } .................................................................................................................................................... Metoda createTextNode() jest dostępna wyłącznie w obiekcie document i nie ma żadnego bezpośredniego powiązania z jakimkolwiek konkretnym węzłem.

Dynamiczne opcje nawigacyjne to świetna rzecz Nowe opcje nawigacyjne z krótkimi opisami tekstowymi są znacznie bardziej intuicyjne od wcześniejszych enigmatycznych opisów przycisków, czyli cyfr 1 i 2.

Dynamiczne, opisowe, rewelacyjne!

Nowe, dynamiczne opcje decyzyjne precyzyjnie informują użytkownika o tym, jakie możliwości wyboru są dostępne w danej scenie.

386

Rozdział 8.

Modyfikacje stron WWW

Nie ma

niemądrych pytań

P: Dlaczego w nowym

interfejsie użytkownika Przygody kreskowego ludzika zostały zastosowane elementy span, a nie div?

O: Ponieważ obie opcje decyzyjne muszą

być wyświetlone jedna obok drugiej; a to oznacza, że nie mogą być elementami blokowymi. Elementy div są elementami blokowymi, a elementy span — elementami wewnątrzwierszowymi. Właśnie z tego powodu zastosowaliśmy elementy span.

P: Kiedy utworzę węzeł przy

użyciu metody createTextNode(), to gdzie zostanie on umieszczony?

O: Nigdzie. Bezpośrednio po utworzeniu

węzła tekstowego znajduje się on w miejscu poza przestrzenią, przynajmniej jeśli chodzi o przestrzeń drzewa DOM na danej stronie. Węzeł zostanie umieszczony w drzewie DOM i wyświetlony na stronie dopiero w chwili, gdy jawnie dodasz go jako węzeł potomny do jakiegoś innego elementu.

P: Czy zawartość węzła

tekstowego, tworzonego przy użyciu metody createTextNode(), musi być prostym tekstem — bez jakichkolwiek znaczników HTML?

O: Tak. DOM nie działa tak jak

właściwość innerHTML, w której możesz zapisać dowolny kod HTML. Kiedy DOM mówi o węźle tekstowym, to naprawdę ma na myśli zwyczajny, prosty tekst, bez jakichkolwiek znaczników HTML ani innych znaczników formatujących.

Interaktywne opcje decyzyjne są jeszcze lepsze Nasze nowe opcje decyzyjne zaimplementowane w Przygodzie kreskowego ludzika są zdecydowanie lepsze od stosowanych wcześniej niewiele mówiących przycisków; nie oznacza to jednak, że nie można by ich jeszcze bardziej ulepszyć. Na przykład można by podświetlać opcje po wskazaniu ich myszką, tak by użytkownik wiedział, że można je kliknąć. Element decyzyjny jest podświetlany, kiedy użytkownik najedzie na niego wskaźnikiem myszki.

Myślałam, że podświetlanie oraz inne wyszukane efekty wizualne są związane ze stylami CSS, a nie z DOM.

Oczywiście, podświetlanie jest związane ze stylami CSS, lecz dostęp do nich wciąż jest realizowany za pośrednictwem DOM. To fakt, że za podświetlanie zawartości strony odpowiadają style CSS; wynika to z faktu, że właśnie style odpowiadają za takie aspekty wyglądu jak czcionka i kolor elementu. Jednak nawet określając style, nie unikniemy spotkania z DOM, a to dlatego, że to właśnie on zapewnia programistyczny dostęp do stylów elementów stron WWW…

jesteś tutaj  387

Style a DOM

Kwestia stylu: CSS i DOM Style CSS są powiązane z elementami HTML, a DOM zapewnia dostęp do stylów za pośrednictwem elementów (węzłów). Używając DOM do modyfikowania wartości stylów CSS, mamy jednocześnie możliwość dynamicznego modyfikowania sposobu prezentacji zawartości strony. Jednym z miejsc, w jakich DOM udostępnia style CSS, jest właściwość style elementu; jest to grupa (klasa) stylów odnoszących się do danego elementu.

Rozpocznij grę



W DOM nazwę klasy CSS, jaka została przypisana do danego elementu, można odczytać przy użyciu właściwości className obiektu węzła. alert(document.getElementById("decision1").className); wnia Właściwość className węzła zape dostęp do klasy CSS przypisanej danemu elementowi.

Właściwość className węzła zapewnia dostęp do klasy CSS przypisanej danemu elementowi. 388

Rozdział 8.

Oglądaj to!

Nie myl klas CSS z klasami JavaScriptu.

Klasy stylów CSS oraz klasy JavaScriptu to dwie zupełnie różne rzeczy. Klasa CSS jest kolekcją stylów, określających postać elementu na stronie WWW, natomiast w języku JavaScript klasa to swoisty wzorzec do tworzenia obiektów. Wszelkie tajniki klas i obiektów w JavaScripcie poznasz w rozdziale 10.

Modyfikacje stron WWW

Podmienianie stylów Aby zmienić wygląd elementu poprzez skojarzenie go z zupełnie inną klasą CSS, wystarczy przypisać mu nazwę nowej klasy. document.getElementById("decision1").className = "decisioninverse"; Ten sam element, ale inna klasa CSS! Poprzez zapisanie nowej wartości we właściwości className zost ała zmieniona klasa CSS określająca postać elementu.

Klasa decisioninverse zmienia schemat kolorów używanych do prezentacji tekstu w elemencie.



Zmiana klasy elementu poprzez zmianę wartości właściwości className spowoduje natychmiastową aktualizację wyglądu elementu i dostosowanie go do stylów zdefiniowanych w nowej klasie. Technika ta pozwala na wprowadzanie w wyglądzie strony zmian o niezwykle szerokim zakresie przy stosunkowo niewielkim nakładzie prac programistycznych.

Zaostrz ołówek Korzystając z dwóch zdarzeń związanych z myszką — onmouseover oraz onmouseout — dodaj do elementów decyzyjnych na stronie Przygody kreskowego ludzika kod, który będzie tworzył efekt podświetlenia w momencie umieszczenia wskaźnika myszy na elemencie. Podpowiedź: klasa CSS, jaką należy tym razem zastosować, nosi nazwę decisionhover.

Rozpocznij grę

jesteś tutaj  389

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie r Klasa CSS decisionhove jest stosowana nie w  odpowiedzi na zdarze er. eov ous onm

Zdarzenie to jest zgłaszane, gdy wskaźnik myszy zostanie umieszczony w obszarze zajmowanym przez element span.

Korzystając z dwóch zdarzeń związanych z myszką — onmouseover oraz onmouseout — dodaj do elementów decyzyjnych na stronie Przygody kreskowego ludzika kod, który będzie tworzył efekt podświetlenia w momencie umieszczenia wskaźnika myszy na elemencie. Podpowiedź: klasa CSS, jaką należy tym razem zastosować, nosi nazwę decisionhover. Rozpocznij grę
Normalny, „niepodświetlony” styl jest odtwarzany w odpowiedzi na zgłoszenie zdarzenia onmouseout.

onmouseout="this.className = 'decision'" .......................................>
To zdarzenie jest zgłaszane, gdy wskaźnik myszy zostanie usunięty z obszaru elementu span.

Nie ma

niemądrych pytań

Klasowe opcje

P: Czy nie można stworzyć przycisków z efektem

Zastosowanie w kodzie Przygody kreskowego ludzika dwóch klas CSS sprawiło, że elementy decyzyjne mają dwa alternatywne sposoby prezentacji: normalny oraz podświetlony.

O: Owszem, można. I w wielu przypadkach jest to znacznie lepszy sposób



background-color:#EEEEEE; }

Normalny

390

Rozdział 8.

Podświetlony

Modyfikacje stron WWW

Test stylizowanych opcji decyzyjnych Zatem dzięki zapewnianej przez DOM możliwości zmiany klasy CSS elementu znowu udało się nam poprawić atrakcyjność interfejsu użytkownika aplikacji Przygody kreskowego ludzika. Ela bardzo pozytywnie ocenia aktualną wersję skryptu.

No, no… ten nowy efekt podświetlania przycisku wskazywanego myszką jest naprawdę rewelacyjny!

Teraz elementy decyzyjne są podświetlane po wskazaniu ich myszką.

Nie ma

niemądrych pytań

P: Nie pamiętam, żebyś pisał

oznacza „poza”, „na zewnątrz”, co wyraźnie sugeruje, że zdarzenie jest generowane, gdy wskaźnik myszy zostanie usunięty poza obszar elementu.

O: Tak. Język JavaScript posiada wiele

P: Dlaczego w kodzie

coś wcześniej o zdarzeniach onmouseover oraz onmouseout. Czy to standardowe zdarzenia?

standardowych zdarzeń, o których nie wspominałem. Jednak w przypadku zdarzeń najważniejsze jest, by wiedzieć, jak je obsługiwać, nawet jeśli poza nazwą nie wiemy o nich wiele więcej. W przypadku tych dwóch zdarzeń bez trudu można określić ich przeznaczenie. Wystarczy sprawdzić, że w języku angielskim słowo „over” oznacza „ponad”, a zatem pierwsze ze zdarzeń, onmouseover, będzie generowane, kiedy wskaźnik myszy zostanie umieszczony ponad elementem. Z kolei słowo „out”

modyfikującym style elementów decyzyjnych nie musieliśmy stosować metody getElementById()?

O: Każdy element HTML, na jakim operują

skrypty, jest reprezentowany przez obiekt, a w kodzie HTML tego elementu mamy dostęp do reprezentującego go obiektu; dostęp ten zapewnia słowo kluczowe this. A zatem słowo kluczowe this używane w kodzie Przygody kreskowego ludzika odwołuje się do obiektu węzła elementu

span. Jest to dokładnie ten sam obiekt, do którego możemy się odwołać za pomocą DOM i który posiada właściwość className. A zatem aby przypisać elementowi span nową nazwę klasy, wystarczy użyć wyrażenia this.className.

P: Klasy CSS są OK, ale tak

naprawdę to chciałbym zmienić wartość tylko jednej, wybranej właściwości stylu. Czy to możliwe?

O: No, no… gratulujemy intuicji. To palący

problem w aplikacji Przygody kreskowego ludzika, którego rozwiązanie Ela bardzo chętnie by poznała. Tak się przypadkowo składa, że tajemnica modyfikacji wartości wybranych właściwości stylów CSS znowu jest ściśle związana z JavaScriptem i DOM…

jesteś tutaj  391

Kiedy przyciski nie mają nazwy

Problemy z opcjami — pusty przycisk Dużo czasu minęło, zanim Ela zdecydowała się zmierzyć z tym problemem. Jednak w końcu nastał ten moment i trzeba zabrać się za rozwiązanie dziwacznego problemu pustych opcji decyzyjnych na stronie Przygody kreskowego ludzika. W pewnych scenach można podjąć tylko jedną decyzję, jednak pomimo to wyświetlane są oba decyzyjne elementy span; wyraźnie to widać na poniższym przykładzie. Dla użytkownika taki pusty element decyzyjny, na którym nie ma żadnej informacji o jego przeznaczeniu, może być niepokojący.

Wkurza mnie, że na niektórych scenach pojawiają się puste opcje. Taka pusta opcja nie ma najmniejszego sensu i jedynie wprowadza zamieszanie.

Pusty element decyzyjny jest dziwny i mylący.

WYTĘŻ umysł

W jakich innych scenach występuje ten sam problem? W jaki sposób można by go rozwiązać?

392

Rozdział 8.

Modyfikacje stron WWW

Modyfikacja stylów wedle zamówienia Czasami zmiana klasy przypisanej elementowi jest zbyt radykalnym rozwiązaniem. W przypadku gdy konieczna jest nieco większa precyzja i szczegółowość, można skorzystać z obiektu style. Jest on dostępny jako właściwość style obiektu węzła i zapewnia dostęp do konkretnych właściwości stylów CSS; na przykład do właściwości visibility, która określa, czy element jest widoczny, czy nie. W przypadku aplikacji Przygoda kreskowego ludzika drugi element decyzyjny można początkowo ukryć, określając odpowiednią wartość właściwości visibility bezpośrednio w kodzie HTML strony:
Właściwość style węzła zapewnia dostęp do poszczególnych właściwości stylów.

onmouseover=”this.className = ‘decisionhover’” onmouseout=”this.className = ‘decision’” style=”visibility:hidden”>


Teraz wyświetlenie bądź ukrycie elementu sprowadza się do przypisania odpowiedniej wartości właściwości visibility — visible, aby element był widoczny, bądź hidden, aby go ukryć. document.getElementById("decision2").style.visibility = "visible"; document.getElementById("decision2").style.visibility = "hidden";

paf!

Zaostrz ołówek

Druga opcja.

W niektórych scenach Przygody kreskowego ludzika podczas wyświetlania nowej sceny konieczne jest określenie widzialności drugiego elementu decyzyjnego. Zakreśl te sceny na poniższym schemacie i dodaj krótką notatkę do decyzji, w przypadku gdy nowa scena wymaga zmiany widoczności którejś z opcji decyzyjnych. Scena nr 4

Scena nr 2

Mały domek w lesie

Scena nr 0

Wprowadzenie

Scena nr 1

Wiedźma w oknie

Scena nr 8

Ciąg dalszy nastąpi…

Scena nr 5

Zjedzony przez wiedźmę. KONIEC Scena nr 6

Rozwidlenie dróg Scena nr 3

Most nad potokiem

Zjedzony przez trolla. KONIEC Scena nr 7

Troll na moście.

Scena nr 9

Ciąg dalszy nastąpi…

jesteś tutaj  393

Rozwiązanie ćwiczenia

Zaostrz ołówek W niektórych scenach Przygody kreskowego ludzika podczas wyświetlania nowej sceny konieczne jest określenie widzialności drugiego elementu decyzyjnego. Zakreśl te sceny na poniższym schemacie i dodaj krótką notatkę do decyzji, w przypadku gdy nowa scena wymaga zmiany widoczności którejś z opcji decyzyjnych.

Druga opcja.

Drugi element decyzyjny należy ukryć w każdej scenie, która prowadzi do kolejnej sceny pozwalającej na podjęcie tylko jednej decyzji (takiej jak rozpoczęcie nowej gry).

Wiedźma w oknie

Scena nr 2

Mały domek w lesie

Wyświetlamy. Scena nr 0

Wprowadzenie

Scena nr 4

Scena nr 5

Ukrywamy.

Scena nr 1

Zjedzony przez wiedźmę. KONIEC

Scena nr 8

Ciąg dalszy nastąpi…

Ukrywamy.

Scena nr 6

Rozwidlenie dróg Scena nr 3

Ukrywamy.

Most nad potokiem

Drugi element decyzyjny musi być wyświetlony raz podczas rozpoczynania nowej gry.

Zjedzony przez trolla. KONIEC Scena nr 7

Troll na moście.

Ukrywamy.

Scena nr 9

Ciąg dalszy nastąpi…

Kiedy gra się kończy, drugi element decyzyjny należy ukryć, jednak ukrycie to powinno nastąpić już w scenie, która prowadzi do sceny kończącej Przygodę kreskowego ludzika.

W każdej ze scen musimy odpowiednio wyświetlać lub ukrywać drugi element decyzyjny, wykorzystując przy tym właściwość visibility obiektu style.

... case 7: if (decision == 1) { curScene = 6 m dla trolla."; message = "Przykro mi, właśnie stałeś się smacznym obiadkie "; decision1 = "Zacznij ponownie decision2 = ""; // Ukrywamy drugi element decyzyjny ; document.getElementById("decision2").style.visibility = "hidden" } else { curScene = 9; decision1 = "?"; decision2 = "?"; } break; ...

394

Rozdział 8.

Modyfikacje stron WWW Kluczowe zagadnienia 

Właściwość className węzła pozwala modyfikować jego wygląd w dowolnie dużym zakresie poprzez umożliwienie zmiany klasy stylów CSS przypisanej danemu węzłowi.



Właściwość style węzła pozwala wprowadzać niewielkie zmiany w wyglądzie węzła poprzez zapewnienie dostępu do właściwości stylów CSS.



Klasy CSS nie mają absolutnie nic wspólnego z klasami w języku JavaScript — są to dwie całkowicie odrębne rzeczy.



Właściwość visibility obiektu style pozwala na dynamiczne wyświetlanie i ukrywanie elementu na stronie. Podobny efekt wyświetlania i ukry można uzyskać przy użyciu właś wania ciwości display; przypisanie jej wartości none powoduje ukrycie elementu, a wart ości block — wyświetlenie.

Żadnych niepotrzebnych opcji Manipulowanie konkretnymi właściwościami stylów przy użyciu DOM pozwala na selektywne wyświetlanie i ukrywanie drugiego elementu decyzyjnego. W efekcie możemy sprawić, że interfejs użytkownika Przygody... będzie znacznie bardziej sensowny, gdyż nie będą na nim prezentowane żadne niepotrzebne, puste elementy.

Ach, znacznie lepiej… te puste opcje decyzyjne były bardzo denerwujące!

Teraz, kiedy drugi element decy zyjn nie jest potrzebny (jak na przykład y w scenie tytułowej), ukrywamy go.

jesteś tutaj  395

Duże drzewo decyzyjne

Więcej opcji, większa złożoność Ela wyobraża sobie, że Przygoda kreskowego ludzika będzie się dynamicznie rozwijać — wzbogacać o nowe, fascynujące sceny i decyzje. Tak się składa, że DOM możne nam pomóc w zarządzaniu znacznie bardziej złożonym drzewem decyzyjnym Przygody kresowego ludzika. Scena nr 10

...

Scena nr 4

Scena nr 2

Mały domek w lesie Scena nr 1

Scena nr 0

Wprowadzenie

Wiedźma w oknie

Scena nr 8

...

... Scena nr 5

Koniec

Zjedzony przez wiedźmę. KONIEC Scena nr 6

Rozwidlenie dróg Scena nr 3

Most nad potokiem

Scena nr 11

Scena nr 12

Zjedzony przez trolla. KONIEC Scena nr 7

Troll na moście.

... Scena nr 9

...

Scena nr 13

...

Ciekawsza i dłuższa przygoda = większe drzewo decyzyjne!

* Najnowsza wersja Przygody kreskowego ludzika czeka na serwerze FTP wydawnictwa Helion i jest gotowa, byś ją pobrał i ułatwił sobie życie. Oto jej adres: ftp://ftp.helion.pl/przyklady/hfjsc.zip. Pobierz ją już teraz, jeśli nie zrobiłeś tego wcześniej.

396

Rozdział 8.

Modyfikacje stron WWW Scena nr 28 Scena nr 24 Scena nr 18

...

Scena nr 14

...

...

Scena nr 29

...

Scena nr 19

... Koniec Scena nr 20

...

Scena nr 15

Scena nr 21

...

...

Scena nr 25

...

... Scena nr 30

... Scena nr 26

Koniec

... Scena nr 31

Scena nr 27

...

... Scena nr 22

Koniec

... Scena nr 16

Koniec Scena nr 23

...

...

O rany! To cała masa decyzji… i wygląda na to, że przetestowanie tego będzie istnym koszmarem.

Scena nr 17

... Koniec

WYTĘŻ

Duże przygody bez wątpienia mogą się stać dużym problemem, o ile nie będziemy dysponowali jakimś sposobem przetestowania drzewa decyzyjnego. Wraz z rozwojem opowieści i dodawaniem do niej kolejnych scen i decyzji testowanie logiki i sprawdzanie, że konkretne decyzje prowadzą w odpowiednie miejsca drzewa, może się stawać coraz trudniejsze. Nasza Przygoda kreskowego ludzika coraz pilniej wymaga opracowania jakiegoś sposobu pozwalającego na analizowanie ścieżek, czyli różnych sposobów przejścia opowieści.

umysł

Jaki według Ciebie będzie najlepszy sposób stworzenia testowych ścieżek w tak monstrualnym drzewie decyzyjnym?

jesteś tutaj  397

Poprowadzi nas ścieżka

Śledzenie drzewa decyzyjnego Podobnie jak narzędzie historii w przeglądarce WWW, które zapamiętuje odwiedzone strony, tak i mechanizm historii w Przygodzie kreskowego ludzika będzie można wykorzystać do sprawdzenia i przeanalizowania przebiegu przygody. Założeniem tego mechanizmu jest umożliwienie wyświetlania sekwencji decyzji, które doprowadziły do konkretnego wyniku. Taki mechanizm pomógłby Eli zapewnić, że ścieżki decyzyjne w drzewie Przygody... wiodą tam, gdzie powinny.

1

Wiedźma w oknie

1

Wprowadzenie

1

1 ...

2

Scena nr 11

...

Mały domek w lesie

Scena nr 5

Koniec

Zjedzony przez wiedźmę. KONIEC

Scena nr 1

Scena nr 0

...

Scena nr 8

Scena nr 4

Scena nr 2

Scena nr 10

Scena nr 6

Rozwidlenie dróg

Scena nr 12

Zjedzony przez trolla. KONIEC

Scena nr 3

Most nad potokiem

... Scena nr 9

Scena nr 7

Troll na moście.

...

Scena nr 13

...

Historia decyzji jest listą wybieranych opcji oraz scen prezentowanych w ramach danej ścieżki opowieści. Ela może z niej później skorzystać do prześledzenia wybieranych opcji i scen.

Start Wybrano decyzję numer 1…

Scena nr 0 — Wprowadzenie.

1

Scena nr 1 — Rozwidlenie dróg.

1

Scena nr 2 — Mały domek w lesie.

1

Scena nr 4 — Wiedźma w oknie.

1

Scena nr 8 — …

2

Scena nr 11 — …

Koniec

398

Rozdział 8.

…która prowadzi do sceny nr 4.

Każda wyświetlona scena jest dodawana do historii decyzji, podobnie jak każda podjęta przez użytkownika decyzja.

WYTĘŻ umysł

Jakie zmiany należy wprowadzić w Przygodzie kreskowego ludzika, by zaimplementować mechanizm rejestrowania historii decyzji?

Modyfikacje stron WWW

Przekształć historię swoich decyzji na kod HTML Patrząc na zagadnienie decyzji z punktu widzenia HTML-a, napisanie kodu niezbędnego do rejestrowania historii nie jest wyjątkowo trudnym zadaniem. Element div i akapit tekstu dla każdej decyzji w zupełności wystarczą.

Każdy element p stanowi jeden wpis na liście historii i zawiera podjętą przez użytkownika decyzję.

Decyzja 1. -> Scena nr 1: Rozwidlenie dróg.

Decyzja 2. -> Scena nr 2: Mały domek w lesie.

Decyzja 3. -> Scena nr 4: Wiedźma w oknie.



Mechanizm rejestracji

A zatem nie pozostaje nic innego, jak tylko napisać kod JavaScript wykorzystujący DOM do wygenerowania węzłów, które na stronie utworzą listę z historią decyzji.

To szaleństwo. Przecież nie możesz dowolnie tworzyć nowych akapitów tekstu… a może możesz?

historii w Przygodzie kreskowego ludzika może być bardzo przydatnym narzędziem do testowania aplikacji.

DOM pozwala na tworzenie dowolnego elementu HTML w dowolnej chwili, dotyczy to także akapitów tekstu. Okazuje się jednak, że tworzenie akapitów jest możliwe. Operacja ta wymaga zastosowania kolejnej metody obiektu document — createElement(). Metoda ta pozwala na tworzenie dowolnych elementów HTML. A zatem rozwiązanie problemu historii decyzji będzie polegać na utworzeniu nowego elementu akapitu przy użyciu metody createElement(), a następnie dodaniu do niego odpowiedniego tekstu poprzez wywołanie metody createTextNode(). W efekcie utworzymy całkowicie nową gałąź węzłów, którą następnie dodamy do drzewa węzłów całej strony. div

document.createElement("p");

document.createTextNode("...");

ELEMENT

p

text e dróg." "Decyzja 1. -> Scena nr 1: Rozwidleni

jesteś tutaj  399

Zabawy z kodem HTML

Produkcja kodu HTML Utworzenie nowego elementu HTML przy użyciu metody createElement() wymaga jedynie podania nazwy znacznika. A zatem stworzenie akapitu (p) sprowadza się do wywołania tej metody i przekazania w jej wywołaniu łańcucha znaków "p”. Nie można także zapomnieć o zapisaniu w jakiejś zmiennej nowego elementu zwróconego przez metodę.

Zaczynamy od utworzenia nowe go elementu p, który wolno unosi się w przestrzeni strony.

var decisionElem = document.createElement("p");

p

A zatem dysponujemy już nowym elementem akapitu, który na razie nie ma żadnej zawartości ani nie należy do drzewa węzłów strony. Teraz dodamy do niego tekst. W tym celu musimy utworzyć węzeł tekstowy i dodać go do węzła p jako jego element potomny. decisionElem.appendChild(document.createTextNode("Decyzja 1. -> Scena nr 1: Rozwidlenie dróg.")); p

Element p wciąż jest niezależny, jednak teraz, po dodaniu nowego węzła tekstowego, ma już jakąś zawartoś ć.

e dróg." "Decyzja 1. -> Scena nr 1: Rozwidleni

Ostatnim krokiem będzie dodanie nowego akapitu do strony jako potomka elementu div zawierającego listę historii decyzji. document.getElementById("history").appendChild(decisionElement); Element p zostaje dodany do dokumentu jako potomek istniejąc elementu div, co powoduje doda ego nie go do drzewa węzłów strony i wyświetlenie.

...

Decyzja 1. -> Scena nr 1: Rozwidlenie dróg.

...

p

dróg. Decyzja 1. -> Scena nr 1: Rozwidlenie

Wykonując opisane powyżej czynności za każdym razem, gdy użytkownik przejdzie do nowej sceny w Przygodzie kreskowego ludzika, możemy dynamicznie utworzyć historię decyzji.

400

Rozdział 8.

div

Modyfikacje stron WWW

Kluczowe zagadnienia 

Element HTML można utworzyć, używając metody createElement() obiektu document.



Aby określić zawartość elementu, należy utworzyć węzeł tekstowy i dodać go do elementu.



Wszystkie operacje na drzewie DOM, takie jak dodawanie i usuwanie jego węzłów, należy wykonywać bardzo uważnie — za ich pomocą można bowiem dowolnie usuwać i dodawać gałęzie tego drzewa.

Zaostrz ołówek Do funkcji changeScene() dodaj kod obsługujący dynamiczne tworzenie historii decyzji. Podpowiedź: zawsze, gdy aktualną sceną nie jest scena nr 0, musisz dodać do elementu z historią decyzji nowy element akapitu wraz z węzłem tekstowym; natomiast w przeciwnym przypadku, gdy właśnie przeszliśmy do sceny nr 0, musisz wyczyścić całą listę historii.

function changeScene(option) { ... // Aktualizujemy historię decyzji

........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................ ........................................................................................................................................................................

jesteś tutaj  401

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie Dodajemy nowy węzeł tekstowy do nowego elementu akapitu.

Do funkcji changeScene() dodaj kod obsługujący dynamiczne tworzenie historii decyzji. Podpowiedź: zawsze, gdy aktualną sceną nie jest scena nr 0, musisz dodać do elementu z historią decyzji nowy element akapitu wraz z węzłem tekstowym; natomiast w przeciwnym przypadku, gdy właśnie przeszliśmy do sceny nr 0, musisz wyczyścić całą listę historii.

function changeScene(option) { ... // Aktualizujemy historię decyzji

Pobieramy element div z historią decyzji, używając w tym celu jego identyfikatora.

function changeScene(decision) { ........................................................................................................................................................................ ... ........................................................................................................................................................................ // Aktualizujemy historię decyzji ........................................................................................................................................................................ var history = document.getElementById("history"); ........................................................................................................................................................................ if (curScene != 0) { ........................................................................................................................................................................ // Dodajemy ostatnią decyzję do historii ........................................................................................................................................................................ var decisionElem = document.createElement("p"); ........................................................................................................................................................................ decisionElem.appendChild(document.createTextNode("Decyzja " + decision + ". -> ........................................................................................................................................................................ Scena nr " + curScene + ". : " + message)); ........................................................................................................................................................................ history.appendChild(decisionElem); ........................................................................................................................................................................ Dodajemy element akapitu do elementu div, a ten z kolei dodajemy do drzewa węzłów strony.

} ........................................................................................................................................................................ else { ........................................................................................................................................................................ // Czyścimy historię ........................................................................................................................................................................ while (history.firstChild) ........................................................................................................................................................................ history.removeChild(history.firstChild); ........................................................................................................................................................................ } ........................................................................................................................................................................ } ........................................................................................................................................................................ W celu wyczyszczenia historii mne usuwamy wszystkie elementy poto div. entu elem o nieg odpowied

402

Rozdział 8.

Tworzymy nowy węzeł tekstowy zawierający informacje o podjętej decyzji.

Modyfikacje stron WWW

Śledzenie przebiegu przygody Mechanizm rejestrowania historii zaimplementowany w Przygodzie kreskowego ludzika pozwala na uważne śledzenie toku opowieści w trakcie jej rozwoju.

Przygoda jeszcze się nie zaczęła, zatem nie ma żadnej historii.

Historia decyzji jest rewelacyjna! W końcu mogę w pełni rozwinąć swoją kreatywność, nie tracąc przy tym kontroli nad drzewem decyzyjnym.

Wraz z rozwojem przygody historia się wydłuża.

Tworzenie ścieżki historii zostaje zakończone wraz z dotarciem do końca przygody.

jesteś tutaj  403

Niekończąca się opowieść

Długa i dziwna podróż… Czas, byś wypróbował swoją kreatywność i rozbudował Przygodę kreskowego ludzika do rozmiarów, które nadadzą sens mechanizmowi rejestrowania historii opowieści. Twój kreskowy ludzik jest już gotowy do drogi i wprost nie może się doczekać nowych przygód…

ćwiczenie

404

Wymyśl swoją własną kontynuację Przygody kreskowego ludzika i dodaj odpowiedni kod do naszej przykładowej aplikacji, tak byś mógł ją opublikować w internecie ku uciesze innych miłośników przygód. To ćwiczenie nie ma rozwiązania… po prostu baw się dobrze, wymyślając nowe przygody!

Rozdział 8.

Modyfikacje stron WWW

Zaginarka stron Zegnij stronę wzdłuż pionowych linii, tak by oba mózgi się połączyły, a następnie rozwiąż zagadkę.

Czym jest DOM?

To spotkanie umysłów!

document

html
ng> mylony

ng>częstojest
body

head

z przeglądarką, która

""



"The DOM"

"jest"

span

strong

"często"

nim zarządza
.

div

""

"przeglądarką,"

"mylony z"

span

em

"nim zarządza"

"która"

Programiści używający JavaScript muszą bardzo uważać, by nie ulec zbytniej fascynacji modelem DOM i jego możliwościami. Nie staraj się jednak zostać całkowitym manipulatorem — w przeciwnym razie możesz wywołać niezłe zamieszanie w swoich węzłach i zniszczyć pieczołowicie tworzonyukład graficzny i wygląd dokumentów HTML

jesteś tutaj  405

406

Rozdział 8.

9. Ożywianie danych

Obiekty jako Frankendane Kiedyś przy użyciu tego oto narzędzia udało mi się rozmontować człowieka… Sam możesz go spytać… Poskładałem go potem z powrotem w jedną całość.

Obiekty JavaScriptu nie są tak makabryczne, jak dobry lekarz mógłby przypuszczać. Niemniej są ciekawe, gdyż łączą różne elementy języka w jedną całość, która ma większe możliwości niż suma jej składowych. Obiekty łączą dane z akcjami, tworząc w ten sposób nowy typ danych, który w znacznie większej mierze niż opisane wcześniej typy przypomina coś „żywego”. W efekcie możemy stworzyć tablice, które same będą potrafiły posortować swoją zawartość, łańcuchy znaków dysponujące możliwością samodzielnego przeszukiwania swojej treści, a skryptom może wyrosnąć sierść i mogą zacząć wyć do księżyca! No dobra, te dwie ostatnie rzeczy nie są możliwe, ale rozumiesz już, o co chodzi.

to jest nowy rozdział  407

Czas na imprezkę

JavaScriptowa impreza Planujemy imprezkę, a Ty jesteś odpowiedzialny za zaproszenia. Odpowiedzmy sobie zatem na pierwsze pytanie: „Jakie informacje muszą się znaleźć na doskonałym zaproszeniu?”.

Co?

Kto? Zapraszana osoba

Gdzie?

Kiedy?

Impreza puzzlowa

Lokalizacja

Data i godzina

Dane zaproszenia. Czynności związane z zaproszeniami

Prezentacja danych!

Dostarczenie! Dostarczenie danych na zaproszenia

Przedstawienie danych zaproszenia

Takie zaproszenie na imprezkę stworzone w JavaScripcie zawierałoby zmienne odpowiadające danym oraz funkcje reprezentujące akcje. Problem polega jednak na tym, że w rzeczywistym świecie dane nie są w żaden sposób odseparowane od akcji. Co?

Kto? Zapraszamy Cię na…

Imprezę puzzlową! Data: Miejsce:

24 października ul. Wy tężonego umysłu 12/34 Geniuszowice 12-345 Polska Gdzie?

Kiedy?

Prezentacja danych

W rzeczywistym świecie papierowe zaproszenie łączy w sobie zarówno dane, jak i czynności, tworząc na ich podstawie jedną całość — obiekt.

408

Rozdział 9.

Renia, miłośniczka układanek 23 Aleja Wycinanek 78-098 Łamigłowice Polska

Dostarczenie

Ożywianie danych

Dane + akcje = obiekt Nie zawsze w JavaScripcie musisz pracować osobno na danych i osobno na akcjach. Obiekty JavaScriptu łączą w sobie dane i akcje, tworząc nowe, unikalne struktury danych, które potrafią nie tylko przechowywać dane, lecz także na nich operować. Te nowe możliwości pozwalają wykorzystywać w skryptach sposób myślenia stosowany przez nas w rzeczywistym świecie. Pisząc skrypty, możemy zatem myśleć o „rzeczach”, a nie osobno o danych i o akcjach, jakie się na nich wykonuje. Jeśli spojrzysz na zaproszenie jako na obiekt JavaScriptu, uzyskasz:

Obiekt Akcje function prezentuj(co, kiedy, gdzie) { ...

Dane var kto; var co;

+

var kiedy;

}

=

function dostarcz(kto) { ... } Poza obiektem dane

var gdzie;

function prezentuj (co, kiedy, gdzie) { ...

muszą być przekazywane do funkcji w postaci argumentów.

var kto; var co; var kiedy; var gdzie;

} function dostarcz(kto) { ... }

Wewnątrz obiektu zaproszenia, dane i funkcje istnieją wspólnie i są ze sobą powiązane znacznie ściślej niż poza obiektem. Konkretnie rzecz ujmując, funkcje umieszczone w obiekcie mają bezpośredni dostęp do jego zmiennych, dzięki czemu nie trzeba tych informacji przekazywać jako argumentów wywołania.

Obiekty kojarzą kto dostarcz() co

zaproszenie

kiedy

i funkcje, umieszczając je wewnątrz

prezentuj() gdzie

ze sobą zmienne

jednego pojemnika.

Dane umieszczone wewnątrz obiektu zaproszenia są ukryte dla świata zewnętrznego, natomiast funkcje tego obiektu mogą z nich korzystać bez żadnych ograniczeń. Jak zatem widać, obiekt służy jako swoisty kontener, który przechowuje dane i kojarzy je z kodem, który może na nich operować.

jesteś tutaj  409

Członkostwo zapewnia przywileje

Obiekt jest właścicielem danych O zmiennych i funkcjach umieszczonych wewnątrz obiektu mówi się, że są jego składowymi. Konkretnie rzecz ujmując, zmienne umieszczone w obiekcie są nazywane właściwościami, a funkcje — metodami. Nie zmieniają one swego charakteru — właściwości wciąż przechowują dane, a metody wykonują na nich operacje — niemniej wszystko odbywa się już w kontekście konkretnego obiektu.

Zmienne var kto; var co; var kiedy; var gdzie;

Funkcje gdzie) { function prezentuj(co, kiedy, ...

Funkcje metoda stają się mi obie ktu.

} function dostarcz(kto) { ... }

Obiekt Właściwości var var var var

Zmienne stają się właściwościami obiektu.

kto; co; kiedy; gdzie;

Metody function prezentuj() { ... } function dostarcz() { ... }

Właściwości i metody są obiektowymi odpowiednikami

Właściwości i metody „należą” do obiektu, co oznacza, że są przechowywane wewnątrz danego obiektu, tak samo jak dane są przechowywane w tablicy. Jednak w odróżnieniu od tablic dostęp do właściwości i metod obiektów odbywa się zazwyczaj przy użyciu specjalnego operatora — kropki (.).

zmiennych i funkcji.

obiekt Nazwa obiektu.

410

Rozdział 9.

+

.

+

Zwyczajna kropka.

właściwość/metoda Nazwa właściwości lub metody.

Ożywianie danych

W odwołaniach do składowych obiektów używamy kropki Operator kropki tworzy odwołanie pomiędzy właściwością lub metodą a obiektem, do którego dana właściwość lub metoda należy. W zbliżony sposób przedstawiamy się osobom, podając imię, określamy, kim jesteśmy, lecz podając nazwisko, określamy rodzinę, do której należymy. Analogicznie jest z obiektami — nazwa właściwości określa konkretną właściwość, o jaką nam chodzi, natomiast nazwa obiektu określa, do jakiego obiektu właściwość ta należy. A operator kropki łączy ze sobą obiekt z właściwością lub metodą. Teraz możemy już poskładać dane, które chcemy umieścić w obiekcie zaproszenia; wykorzystamy do tego właściwości i operator kropki. Nazwa obiektu.

Kropka!

Nazwa właściwości.

Operator kropki pozwala odwołać się do właściwości lub metody podanego obiektu.

zaproszenie.kto = ”Renia, miłośniczka układanek”; zaproszenie.co

= ”Impreza puzzlowa!”;

zaproszenie.kiedy = ”24 października ”; zaproszenie.gdzie = ”ul. Wytężonego umysłu 12/34”; Operator kropki zapewnia dostęp do poszczególnych właściwości.

Pamiętaj, że w świecie obiektowym dane i akcje są częściami tego samego obiektu, a co za tym idzie — nie trzeba przekazywać do metody żadnych argumentów, by wykonać jakieś operacje na danych przechowywanych we właściwościach obiektu. To sprawia, że wykonywanie operacji na konkretnych obiektach jest całkiem proste: zaproszenie.dostarcz(); Nazwa obiektu.

ćwiczenie

Nazwa metody.

W naszym zaproszeniu brakuje właściwości pozwalającej zapraszanej osobie odpowiedzieć na zaproszenie i zadeklarować, czy z niego skorzysta, czy nie. Napisz kod dodający do obiektu zaproszenia dla Reni właściwość odp (Renia chętnie skorzysta z zaproszenia) oraz metodę wyslijOdp(), która wyśle odpowiedź ................................................................................................................................................................................ ................................................................................................................................................................................

jesteś tutaj 

411

Pytaj, o co chcesz

ćwiczenie

W naszym zaproszeniu brakuje właściwości pozwalającej zapraszanej osobie odpowiedzieć na zaproszenie i zadeklarować, czy z niego skorzysta, czy nie. Napisz kod dodający do obiektu zaproszenia dla Reni właściwość odp (Renia chętnie skorzysta z zaproszenia) oraz metodę wyslijOdp(), która wyśle odpowiedź zaproszenie.odp = "przyjdę"; ................................................................................................................................................................................ zaproszenie.wyslijOdp(); ................................................................................................................................................................................

Operator kropki odwołuje się do właściwości i metody obiektu zaproszenie.

To równie dobrze mogłaby być właściwość logiczna; w takim przypadku wartość true mogłaby oznaczać, że osoba ma zamiar przyjść, a wartość false, że nie ma zamiaru uczestniczyć w spotkaniu

Nie ma

P: Czym właściwie jest obiekt? Czy obiekt ma jakiś typ danych?

O: Owszem, obiekt ma typ danych. Obiekt

jest nazwaną kolekcją właściwości i metod. Właściwie, ściśle rzecz ujmując, należałoby stwierdzić, że obiekty są typem danych. We wcześniejszej części książki poznałeś już kilka typów danych: liczby, łańcuchy znaków oraz wartości logiczne. Są to tak zwane typy proste, gdyż reprezentują jedną, unikalną informację. Z kolei obiekty są nazywane typami złożonymi, gdyż mogą gromadzić w sobie wiele danych. Czyli możesz dodać „obiekt” jako czwartą pozycję na swojej liście typów danych dostępnych w języku JavaScript (zaraz za liczbami, łańcuchami znaków i wartościami logicznymi). A zatem każdy obiekt, którego używasz, niezależnie od tego, czy stworzyłeś go sam, czy jest to wbudowany obiekt JavaScriptu, ma typ danych — jest obiektem.

P: Czy nie mogę używać

zmiennych globalnych i funkcji zamiast właściwości i metod obiektu? W końcu funkcje mają dostęp do zmiennych globalnych, prawda?

O: Owszem, mają dostęp. Problem polega

jednak na tym, że każdy inny kod także będzie mieć dostęp do tych zmiennych globalnych. A to przysparza problemów, ponieważ zawsze zależy nam na tym, by ograniczyć dostępność danych wyłącznie do kodu, który ma na tych danych operować. W ten sposób chronimy

412

Rozdział 9.

niemądrych pytań

się przed potencjalnym niebezpieczeństwem modyfikacji danych przez kod, który nie powinien tego robić. Niestety, język JavaScript w swojej aktualnej postaci nie chroni właściwości przed dostępem z poziomu kodu znajdującego się poza obiektem. Poza tym mogą się zdarzyć sytuacje, w których będziemy chcieli odwołać się do właściwości obiektu w sposób bezpośredni. Niemniej idea jest właśnie taka, że dane umieszczamy w obiekcie właśnie po to, by je z nim skojarzyć. Informacja skojarzona z obiektem ma bowiem znacznie lepszy kontekst i dokładniej określone znaczenie od informacji niezależnej (zapisanej w zmiennej globalnej).

P: Zauważyłem, że w kilku

miejscach książki używaliśmy już notacji obiektowej wykorzystującej kropkę. Czy to znaczy, że już używaliśmy obiektów?

O: Tak. Sam się przekonasz, że w praktyce

trudno jest pisać skrypty w języku JavaScript bez stosowania obiektów. Dzieje się tak dlatego, że JavaScript sam jest jedną wielką kolekcją obiektów. Na przykład z technicznego punktu widzenia funkcja alert() jest metodą obiektu window, co oznacza, że można ją wywoływać, używając zapisu window.alert(). Obiekt window reprezentuje okno przeglądarki i nie trzeba się do niego jawnie odwoływać; dlatego właśnie w tym przypadku wywołanie metody można uprościć do postaci alert().

P: No dobra, to naprawdę

zagmatwane. Czyli twierdzisz, że funkcje są tak naprawdę metodami?

O: Owszem, choć pojmowanie funkcji

właśnie w taki sposób może być mylące i kłopotliwe. Wiesz już, że funkcja to fragment kodu, który można wywołać z innego miejsca skryptu, używając w tym celu nazwy funkcji. Metoda jest zwyczajną funkcją umieszczoną w obiekcie. Problemy zaczynają się w chwili, gdy zdasz sobie sprawę, że w rzeczywistości każda funkcja należy do jakiegoś obiektu. A zatem alert() jest jednocześnie i funkcją, i metodą, co wyjaśnia, dlaczego można ją wywoływać na dwa sposoby; jednak większość metod należy wywoływać przy wykorzystaniu notacji obiektowej. W rzeczywistości każda funkcja JavaScriptu należy do jakiegoś obiektu, przez co każda z nich jest metodą. W wielu przypadkach obiektem tym jest obiekt window. Ponieważ interpreter JavaScriptu zakłada, że jeśli nie został jawnie podany żaden obiekt, to należy domyślnie zastosować obiekt window, jak w przypadku wywoływania metody alert(), zatem nic nie stoi na przeszkodzie, by traktować takie metody jak funkcje. Ich przynależność do obiektu window jest raczej przypadkowa, gdyż nie mają one z nim żadnego logicznego powiązania.

Ożywianie danych Kluczowe zagadnienia 



Obiekty są szczególnym rodzajem struktur danych łączących w sobie dane oraz kod, który na tych danych operuje. Z praktycznego punktu widzenia można stwierdzić, że obiekt jest tak naprawdę zbiorem zmiennych i funkcji, połączonych w jedną strukturę danych.



Po umieszczeniu w obiekcie zmienne stają się właściwościami, a funkcje — metodami.



Do właściwości i metod można odwoływać się, podając nazwę obiektu, kropkę, a po niej nazwę właściwości lub metody.

Blog miłośników sześciennych układanek Adresatką naszego zaproszenia jest Renia — miłośniczka kostki Rubika, która wprost nie może się doczekać spotkania z innymi miłośnikami układanek i puzzli. Jednak Renia nie myśli wyłącznie o imprezach i układaniu swojej kostki — Renia chce stworzyć swój własny blog, na którym mogłaby się dzielić z innymi swoją pasją do sześciennych układanek. Renia jest gotowa, by dzielić się swoją wiedzą z innymi na stronie MagicznaKostka!

Słyszałam, że obiekty ułatwią zarządzanie moim kodem i wprowadzanie w nim zmian. Dzięki temu będę miała więcej czasu na zabawę moimi ulubionymi sześciennymi układankami!

Renia słyszała, że w JavaScripcie można tworzyć własne obiekty, dzięki którym kod staje się solidniejszy oraz łatwiejszy do utrzymania i modyfikacji. Słyszała także, że wiele blogów umiera powolną śmiercią, gdyż ich twórcy czują się zmęczeni ich prowadzeniem. Dlatego też Renia chce od początku właściwie podejść do zagadnienia i stworzyć aplikację MagicznaKostka jako skrypt obiektowy, ma bowiem nadzieję, że własne obiekty pozwolą jej długo i bezproblemowo zarządzać swoim blogiem.

Obiektowa MagicznaKostka

=

więcej czasu na układanie!

jesteś tutaj  413

Magiczna kostka

Analiza elementów aplikacji Renia do tej pory spisywała swoje notatki odręcznie, widziała jednak dostatecznie dużo innych blogów, by wiedzieć, że jej będzie musiał zawierać datę i tekst wpisu. Nie wie natomiast, w jaki sposób można przechowywać te dane w skrypcie. Renia nie ma już ochoty prowadzić zwyczajnego papierowego pamiętnika i pisać go odręcznie! Odręcznie pisany pamiętnik Reni.

....................................................................................

............................

14/08/2008 ............................ Dostałam ........................ właśnie nową, ............ zamówi ............ oną............ ............ układan............ ............ kę. ................ To prawdz ........................ iwe............ cudeńko............ ............ . .................................................... ....................................................................................

Każdy wpis zawiera datę oraz treść.

....................................................................................

............................

19/08/2008 ............................ Udało ............ ........................ mi się już ułożyć tę nową ............ układan ............ kę. ............ Już mnie ........................ ................ znudził ........................ a, więc............ szukam ............ czegoś............ nowego. ............ ........................................ ....................................................................................

Data wpisu.

....................................................................................

............................

16/08/2008 ............................ Już mnie ........................ głowa............ rozbolał ............ a od zabaw ............ nową ............tą kostką................. ........................ Muszę............ ........................ sobie odpoczą ć................................................................. ............ ....................................................................................

Treść wpisu.

Ulubiona sześcienna układanka Reni.

....................................................................................

............................

21/08/2008 ............................ Znalazł ........................ am w ............ internec............ ............ ie układan kę 7×7×7. ............ rany! ................ ............O ............ Zabawa ........................ będzie............ super. ................................................................ ............ ....................................................................................

....................................................................................

............................

....................................................................................

............................

....................................................................................

............................

....................................................................................

............................

....................................................................................

............................

Renia desperacko poszukuje jakiegoś prostego sposobu przechowywania i odczytywania par informacji (data oraz tekst). Wszystko wskazuje na to, że potrzeby Reni dokładnie odpowiadają temu, co mogą zaoferować obiekty JavaScriptu… pozwalają one bowiem łączyć informacje w jedną całość.

Data wpisu + treść wpisu = obiekt Blog

414

Rozdział 9.

Nowy, niestandardowy obiekt pozwala połączyć datę wpisu oraz jego treść w jedną całość.

Ożywianie danych

Niestandardowe obiekty rozszerzają język JavaScript Język JavaScript zawiera wiele bardzo użytecznych wbudowanych obiektów. Kilka spośród nich dokładniej przedstawimy w dalszej części tej książki. Niezależnie jednak od ich przydatności może się zdarzyć, że żaden z nich nie będzie spełniać naszych potrzeb i wymagań. Doskonałym przykładem tego ograniczenia jest blog Reni na stronie MagicznaKostka, ponieważ problemu przechowywania danych blogu nie są w stanie rozwiązać żadne standardowe obiekty JavaScriptu… Można jednak skorzystać z obiektów niestandardowych.

Data Standardowe obiekty JavaScriptu

24 sierpnia 2008

Właśnie tak! Łańcuchy znaków także są obiektami!

uch Łańc ów znak ie właśn ałam ą t n s o o i D w " amó z , ą now ankę. układ ziwe awd To pr ko." cudeń

19:00 u Data wpis . u g lo b o d

Tablica Blog

data 0 1

"14/08/200 8"

"Dostałam w łaśn nową, zamów ie ioną układankę. To prawdziw e cudeńko."

Obiekt niestandardowy wprost idealny na potrzeby blogu MagicznaKostka!

Treść wpisu w blogu. Obiekt Blog pełni rolę złożonego typu danych — łączy w jedną całość dwie informacje.

2 3

Także tablice są obiektami.

treść

Dostałam wła śnie nową, zam To prawdziwe ówioną układa cudeńko. nkę. 19/08/2008 Udało mi się już ułożyć tę Już mnie znu nową układa dziła, więc szukam czegoś nkę. 16/08/2008 Już mnie gło nowego. wa rozbolała Muszę sobie od zabaw tą odpocząć. nową kostką. 21/08/2008 Znalazłam w internecie ukł O rany! Zabawa ada będzie super. nkę 7x7x7. 14/08/2008

Gdyby nie obiekty, można by się zastanawiać nad zapisywaniem wpisów do blogu w  dwuwymiarowej tablicy.

Niestandardowe obiekty pozwalają rozbudować JavaScript o możliwości, które spełnią Twoje specyficzne potrzebny. W przypadku strony, którą planuje Renia, niestandardowy obiekt może odwzorowywać wpis w blogu i zawierać dwie właściwości, pozwalające na zapisanie daty wpisu oraz jego treści. Co więcej, obiekt ten może zawierać metody; wzbogacą one wpisy w blogu o działania, dzięki którym tworzenie ich i zarządzanie nimi będzie bardziej intuicyjne. Niemniej, aby powołać taki niestandardowy obiekt do życia, musimy najpierw dowiedzieć się, w jaki sposób tworzy się obiekty…

jesteś tutaj  415

Ostrzeżenie: teren budowy

Tworzenie własnego niestandardowego obiektu Obiekty są ściśle powiązane z danymi, które należy zainicjować w momencie tworzenia konkretnego obiektu; dlatego też do tworzenia obiektów niezbędna jest specjalna funkcja, nazywana konstruktorem. Każdy niestandardowy obiekt musi posiadać swój własny konstruktor, którego nazwa będzie taka sama jak nazwa obiektu. Konstruktor jest wywoływany podczas tworzenia obiektu w celu jego zainicjowania. W przypadku tworzenia obiektów niestandardowych obowiązek napisania odpowiedniego konstruktora spada na twórcę obiektu, czyli Ciebie.

var kto; var co; var kiedy; var gdzie;

function prezentuj() { ... } function dostarcz() { ... }

Inicjalizacja właściwości.

Utworzenie właściwości i metod.

, kiedy, gdzie); Zaproszenie(kto, co Nazwa konstruktora odpowiada nazwie obiektu. Nowo utworzony obiekt jest gotowy do użycia.

kto = "Ktoś"; co = "Coś"; kiedy = "Kiedyś"; gdzie = "Gdzieś";

Konstruktor odpowiada za utworzenie obiektu.

Zaproszenie

Właściwości var var var var

Metody

kto = "Ktoś"; co = "Coś"; kiedy = "Kiedyś"; gdzie = "Gdzieś";

Aby utworzyć nowy obiekt przy użyciu konstruktora, musisz skorzystać z operatora new. Operator ten wywołuje konstruktor, przez co rozpoczyna proces tworzenia obiektu. Etap tworzenia obiektu związany z konstruktorem bardzo przypomina wywołanie funkcji. Nie ma w tym zresztą nic dziwnego, gdyż faktycznie jest to wywołanie. Niemniej bardzo duże znaczenie ma to, by do rozpoczęcia procesu tworzenia obiektu używać operatora new, a nie samodzielnie i bezpośrednio wywoływać jego konstruktor.

function prezentuj() { ... } function dostarcz() { ... }

Do utworzenia obiektu używamy operatora new.

var zaproszenie = new Zaproszenie("Ktoś", "Coś", "Kiedyś", "Gdzieś"); Nowy obiekt zostaje zapisany w zmiennej.

416

Rozdział 9.

Konstruktor jest wywoływany jak funkcja.

Właściwości obiektu są określane poprzez przekazanie argumentów do konstruktora.

Ożywianie danych

Co jest w konstruktorze? Podstawowym zadaniem konstruktora jest utworzenie właściwości obiektu oraz określenie ich początkowych wartości. Aby wewnątrz konstruktora utworzyć właściwość, trzeba skorzystać z nowego słowa kluczowego języka JavaScript — this. Zastosowanie tego słowa kluczowego pozwala określić przynależność właściwości do obiektu i jednocześnie przypisać tej właściwości podaną wartość początkową. Słowo „this” w języku angielskim oznacza między innymi „ten”; a zatem działanie tego słowa kluczowego w JavaScripcie dokładnie odpowiada jego znaczeniu — określa ono, że podana po nim właściwość należy do „tego”, czyli tworzonego obiektu, a nie jest zwyczajną zmienną globalną.

To właśnie słowo kluczowe this odróżnia właściwość obiektu od zwyczajnej zmiennej.

function Zaproszenie(kto, co, kiedy, gdzie) { this.kto = kto; this.co = co; this.kiedy = kiedy; this.gdzie = gdzie;

Nazwy konstruktorów zawsze zaczynają się wielką literą, podobnie jak nazwy obiektów.

Argumenty konstruktora zostają zapisane we właściwościach tworzonego obiektu.

} Konstruktor jest tworzony tak samo jak każda inna funkcja.

W celu utworzenia i zainicjowania właściwości wewnątrz konstruktora używana jest notacja obiektowa (zapis wykorzystujący operator kropki) i słowo kluczowe this. Gdyby nie to słowo kluczowe, konstruktor nie wiedziałby, że chodzi nam o utworzenie właściwości obiektu. Wykonanie powyższego konstruktora powoduje utworzenie czterech właściwości i przypisanie im wartości przekazanych jako argumenty jego wywołania.

Słowo kluczowe this ma podstawowe znaczenie w przypadku tworzenia właściwości obiektu w konstruktorze.

Zaostrz ołówek Napisz konstruktor obiektu Blog, który utworzy i zainicjuje dwie właściwości tego obiektu — datę (date) oraz treść (body). .................................................................................................................................................................... .................................................................................................................................................................... .................................................................................................................................................................... ....................................................................................................................................................................

jesteś tutaj  417

Uruchom to

Zaostrz ołówek Rozwiązanie

Napisz konstruktor obiektu Blog, który utworzy i zainicjuje dwie właściwości tego obiektu — datę (date) oraz treść (body).

Konstruktor nosi tę samą nazwę co obiekt. function Blog(body, date) { .................................................................................................................................................................... Treść wpisu oraz jeg o przekazane do konstruktdata zostają this.body = body; .................................................................................................................................................................... argumenty jego wywo ora jako łania.

this.date = date; ....................................................................................................................................................................

Słowo kluczowe this oznacza, że chodzi nam o właściwość obiektu.

} .................................................................................................................................................................... Właściwości są inicjowane wartościami przekazanymi do konstruktora.

Powołujemy do życia obiekty blogu Obiekt Blog powoli nabiera kształtu, jednak jeszcze nie został utworzony. Niezależnie od tego, jak wspaniały może się wydawać, to jednak wciąż pozostaje jedynie teorią, którą dopiero trzeba udowodnić w praktyce. Pamiętaj, że konstruktor jedynie określa projekt obiektu, jednak żaden obiekt nie zostanie fizycznie utworzony, dopóki nie zastosujemy operatora new — to właśnie on wywoła konstruktor, co w efekcie doprowadzi do utworzenia obiektu. A zatem nie marnujmy więcej czasu i utwórzmy pierwszy obiekt Blog.

Obiekt Blog

gotowych Możesz skorzystać z jdziesz na serwerze przykładów, które zna , pod adresem lion He FTP wydawnictwa lady/hfjsc.zip. ftp://ftp.helion.pl/przyk

Wpis zapisany w pamiętniku.

................

................................................

................................................

................

........................................ ................................................

14/08/2008 ankę. ........................ układ........ wioną ................ , zamó ........ nową ........ właśn ........ łam ........ie Dosta ........ ........ ................ ................ ................................................ cudeń ........ko. dziwe ........ praw To........ ........ ................

........

................

................................................

................................................

........................................ ................................................

19/08 ......../2008 ................ var blogEntry = new Blog("Dostałam właśnie nową, zamówioną...", "14/08/2008"); mnie ................ Już........ ankę. ........ układ ........ nową........ ć tę ........ ułoży........ już........ mi się........ Udało ................ ................ go. ................................ nowe ........ ś ........ czego m ........ szuka ........ więc ........ ziła, znud ................ ........ ................

"14/08/2008" "Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko."

................

................................................

Blog

................................................

........................ ................................................

16/08/2008 ą................. kostk ........ nową ........ ........ zabaw ........tą lała od ........ głowa mnie Jużfun cti on rozbo Blog(b ody, date)........ { ................ ........ ........ ........ ........ ząć. odpoc................ sobie........ ę........ Musz ........ ... ................

........................................

W celu utworzenia obiektu zostaje wywołany konstruktor Blog.

........ ................................................

................................

................................ } ................ ................................................ ........ /2008 ........ 21/08 ................ ................ rany! ................ O 7. ........ 7×7× ........ ankę ................ układ ecie ........ ........ w intern ........ Znala ................ ........złam ................ ................ ........ ........ ........ e super ......... ........................ będzi wa ........ Zaba ........ ........ ................

................................................

................

................................................

................................................

................

........................................ ................................................

........

Obiekt zostaje ........................................................................................ ........................ utworzo ny. ................................................ ................

................................................

418

Rozdział 9.

........

................................................

........ ................................................

Ożywianie danych

Nie ma

niemądrych pytań

P: Wciąż nie do końca rozumiem tworzenie obiektów. Czy to operator new tworzy obiekt, czy konstruktor?

O: One wspólnie! Operator new

rozpoczyna cały proces tworzenia obiektu, a jednym z jego podstawowych obowiązków jest upewnienie się, że zostanie wywołany konstruktor obiektu. Samo wywołanie konstruktora jako zwyczajnej funkcji nie spowoduje utworzenia obiektu, a użycie operatora new bez określenia konstruktora nie ma żadnego sensu.

P: Czy każdy niestandardowy obiekt musi mieć konstruktor?

O: Tak. Wynika to z faktu, że to właśnie

konstruktor jest odpowiedzialny za utworzenie właściwości obiektu, a zatem bez konstruktora nasz obiekt nie miałby żadnych

właściwości. A tworzenie obiektów bez właściwości jest raczej pozbawione sensu. Istnieje jednak pewien wyjątek od reguły dotyczącej konstruktorów obiektów niestandardowych; dotyczy on tworzenia czysto organizacyjnych obiektów stanowiących kolekcję metod, które nie operują na żadnych właściwościach. W takim przypadku można sobie wyobrazić tworzenie obiektu bez konstruktora — z technicznego punktu widzenia jest to możliwe. Pamiętaj jednak, że taki obiekt nie jest wzorowym przykładem prawidłowego programowania obiektowego, gdyż stanowi jedynie grupę powiązanych ze sobą funkcji. Jednak nawet język JavaScript wykorzystuje takie obiekty organizacyjne, o czym sam się przekonasz w dalszej części tego rozdziału.

P: Czym właściwie jest this? O: To słowo kluczowe języka JavaScript,

służące do odwoływania się do obiektu. Konkretnie rzecz biorąc, this pozwala odwołać się do obiektu z poziomu kodu umieszczonego w tym obiekcie. Tak… to brzmi nieco dziwnie… jakby schizofrenicznie. Kiedy jednak to sobie przemyślisz, wszystko nabierze sensu. Aby nasze rozważania nabrały nieco bardziej realnego kształtu, wyobraź sobie, co się stanie, kiedy zgubisz zegarek i ktoś znajdzie go w pokoju pełnym ludzi. Kiedy ktoś podniesie twoją zgubę, to zapewne zawołasz: „Oddaj, to mój zegarek!”. Jak widać, zastosowałeś zaimek „mój”, by odwołać się do własnej osoby. Co ważniejsze, to krótkie słówko wyjaśnia, że to ty jesteś właścicielem znalezionego zegarka. Słowo kluczowe this działa w identyczny sposób — określa przynależność właściwości do obiektu. A zatem wyrażenie this.date oznacza, że date jest właściwością obiektu, w którego kodzie występuje.

Zaostrz ołówek

................

Utwórz tablicę obiektów Blog i zapisz ją w zmiennej o nazwie blog. Tablica ta ma zawierać wpisy, które Renia chce umieścić na stronie MagicznaKostka. Nie musisz być precyzyjny — wystarczy, że podasz jedynie kilka pierwszych słów z treści każdego wpisu.

................................................

................................................

................

................................................ 14/08/2008 kę. ................ układan............ ioną............ zamów ............ nową, ............ m właśnie ............ Dostała............ ........................ .... ................................................ o. cudeńk............ iwe............ To prawdz ............ ........................

................................................

............................ ................................................

....................................

................

................................................ 19/08/2008 ................ Już mnie kę. ............ układan ............ tę nową ............ ............ Udało mi się już ułożyć . ............................ nowego............ czegoś............ szukam ............ ła, więc............ znudzi............ ........................

................................................

................................................

var blog = [............................................................ ............................................................

................

................................................

................................................

................

................................................ 16/08/2008 kostką................. nową............ ............ ła od zabaw ............tą ............ Już mnie głowa rozbola ć................................................................. ............ sobie odpoczą Muszę............ ........................

................................................

............................................................

................................................

................

................................................

................................................

............................................................];

................

................................................ 21/08/2008 rany! ................ ............ kę 7×7×7. ............O ie układan ............ internec............ łam w ............ Znalaz............ ........................ super. ................................................................ będzie............ Zabawa ............ ........................

................................................

................

................................................

................................................

................

................................................

................................................

................

................................................

................................................

................

................................................

................................................

................

................................................

................................................

jesteś tutaj  419

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie ................

Utwórz tablicę obiektów Blog i zapisz ją w zmiennej o nazwie blog. Tablica ta ma zawierać wpisy, które Renia chce umieścić na stronie MagicznaKostka. Nie musisz być precyzyjny — wystarczy, że podasz jedynie kilka pierwszych słów z treści każdego wpisu.

................................................

................................................

................

................................................ 14/08/2008 kę. ................ układan............ ioną............ zamów ............ nową, ............ m właśnie ............ Dostała............ ........................ .... ................................................ o. cudeńk............ iwe............ To prawdz ............ ........................

................................................

................ ................................................

................................................

................

................................................ 19/08/2008 ................ Już mnie kę. ............ układan ............ tę nową ............ ułożyć ............ ............ mi się już Udało ............ ........................ . ............................ nowego............ czegoś............ szukam ............ ła, więc............ znudzi............ ........................

................................................

................

................................................

................................................

................

................................................ 16/08/2008 kostką................. nową............ ............ ła od zabaw ............tą ............ Już mnie głowa rozbola ć................................................................. ............ sobie odpoczą Muszę............ ........................

................................................

................................................

................

................................................

................................................

var blog = new Blog("Dostałam właśnie nową,…", "14/08/2008"), [............................................................ new Blog("Udało mi się już…", "19/08/2008"), ............................................................ new Blog("Już mnie głowa rozbolała…", "16/08/2008"), ............................................................ new Blog("Znalazłam w internecie…", "21/08/2008") ............................................................];

....

.................................... ................................................

21/08/2008 rany! ................ ............ kę 7×7×7. ............O ie układan ............ Znalazłam w internec............ super. ................................................................ będzie............ Zabawa ............ ........................

........................

Każdy wpis w blogu zostaje utworzony jako obiekt Blog i każdy zawiera własną treść i datę.

................................................

................

................................................

................................................

................

................................................

................................................

................

................................................

................................................

................

................................................

................................................

................

................................................

................................................

MagicznaKostka 1.0 Początkową wersję strony MagicznaKostka utworzymy, łącząc przygotowaną w ostatnim ćwiczeniu tablicę obiektów Blog z kodem prezentującym wpisy. Renia doskonale zdaje sobie sprawę z tego, że jej praca jeszcze się nie zakończyła, jednak blog już działa, a Renia jest całkiem zadowolona z pierwszych uzyskanych efektów.

Dane zapisane w jednym obiekcie Blog są schludnie wyświetlane na stronie MagicznaKostka.

Podoba mi się, że obiekty Blog łączą datę i treść wpisu w jedną całość.

Zerknijmy teraz na kod JavaScript odpowiedzialny za tworzenie obiektów Blog i wyświetlanie ich na stronie.

420

Rozdział 9.

Ożywianie danych

MagicznaKostka pod lupą

MagicznaKostka - blog miłośników przestrzennych układanek

Zapisujemy kod HTML wszystkich wpisów w elemencie div o identyfikatorze "blog".

wo Element div "blog", który początko

nim jest pusty, a potem zostaje w u.

MagicznaKostka - blog miłośników przestrzennych układanek

umieszczona lista wpisów w blog MagicznaKostka


Kliknięcie przycisku spowoduje wyświetlenie wszystkich wpisów w blogu.

jesteś tutaj  421

Dlaczego, ach… dlaczego?

Nie ma

niemądrych pytań

P: Dlaczego na stronie

MagicznaKostka konieczny jest przycisk Pokaż wszystkie wpisy?

O: Wziąwszy pod uwagę aktualny stan

blogu, który zawiera jedynie cztery wpisy, przycisk ten w ogóle nie jest potrzebny. Jednak wraz z rozwojem blogu coraz większego znaczenia będzie nabierać ograniczenie ilości wpisów prezentowanych początkowo na stronie głównej, by ich ogrom nie przeraził odwiedzających. Dlatego też tworzona aplikacja Reni będzie domyślnie pokazywać jedynie pięć pierwszych wpisów. Przycisk Pokaż wszystkie wpisy pozwala na pominięcie tych domyślnych ustawień i wyświetlenie wszystkich wpisów w blogu.

P: Dlaczego do wyświetlenia

wpisów została zastosowana właściwość innerHTML, a nie mechanizmy udostępniane przez DOM?

O

: Choć ze względu na zgodność ze standardami na pewno preferowane są obecnie mechanizmy DOM, to jednak stosowanie ich do wyświetlania rozbudowanego kodu HTML zawierającego wiele znaczników jest stosunkowo niewygodne. Wynika to z faktu, że każdy element pojemnika, taki jak

lub , musi zostać utworzony jako element DOM wraz z odpowiednimi węzłami potomnymi, w których zostanie umieszczona jego zawartość. W takich przypadkach

zastosowanie właściwości innerHTML zapewnia niesłychaną wygodę i znacznie upraszcza kod aplikacji.

P

: Dlaczego obiekt Blog nie ma żadnych metod?

O: Ambitny… to dobrze! Prawda jest jednak

taka, że jest wiele innych aspektów działania strony MagicznaKostka, o które musimy zadbać, zanim implementacja metod w obiekcie Blog nabierze jakiegokolwiek znaczenia. Ale nie przejmuj się, bez wątpienia metody należą do długofalowego planu tworzenia strony. W końcu metody są ważną częścią każdego dobrze zaprojektowanego obiektu, dotyczy to oczywiście także naszego obiektu Blog.

Nieuporządkowany blog Strona MagicznaKostka w wersji 1.0 wygląda całkiem dobrze, nie jest jednak pozbawiona wad. Renia zauważyła, że wpisy są wyświetlane na stronie w niewłaściwej kolejności — na samym początku powinien się bowiem znajdować najnowszy wpis. Obecnie wpisy są wyświetlane w takiej samej kolejności, w jakiej zapisaliśmy je w tablicy, a bynajmniej nie możemy oczekiwać, że będzie ona zgodna z kolejnością chronologiczną.

Właśnie zdałam sobie sprawę z tego, że nie zawsze zapisuję wpisy w blogu w kolejności chronologicznej… To jest pewien problem!

Użytkownicy oczekują, że na samym początku blogu znajdzie się najnowszy wpis.

422

Rozdział 9.

Wpisy w blogu powinny być prezentowane w odwrotnej kolejności chronologicznej — od najnowszego do najstarszego.

Ożywianie danych

Potrzeba sortowania Renia ma pomysł na rozwiązanie problemu kolejności wpisów w blogu — chce posortować tablicę wpisów na podstawie daty. Ponieważ w języku JavaScript dostępne są pętle i mechanizmy porównywania, zatem nie powinno być problemu z przeanalizowaniem w pętli wszystkich wpisów, porównaniem ich dat i posortowaniem w odwrotnej kolejności chronologicznej (zaczynając do najnowszego wpisu). Blog Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko.

1

14/08/2008

Przejrzenie w pętli wszystkich wpisów w tablicy blogu.

2 Porównanie daty każdego wpisu z datą

Blog

Zmieniamy kolejność tych dwóch wpisów, gdyż drugi jest nowszy do pierwszego.

Udało mi się już ułożyć tę nową układankę. Już mnie znudziła, więc szukam czegoś nowego. 19/08/2008

następnego wpisu.

Ten wpis powinien się pojawić jako pierwszy, gdyż jest najnowszy.

Blog Już mnie głowa rozbolała od zabaw tą nową kostką. Muszę sobie odpocząć.

3 Jeśli następny wpis jest nowszy od bieżącego, to należy je

16/08/2008

zamienić miejscami. Taki sposób sortowania wpisów w blogu wydaje się sensowny i powinien działać, zakładając oczywiście, że dopracujemy szczegóły związane z porównywaniem dat.

Blog Znalazłam w internecie układankę 7×7×7. O rany! Zabawa będ zie super. 21/08/2008

Chwileczkę! Skoro daty są zapisywane w postaci łańcuchów znaków, to w jaki sposób możemy je porównywać i określać, która z nich jest najnowsza, a która najstarsza?

Data zapisana w postaci łańcucha znaków tak naprawdę wcale nie jest datą. W strategii sortowania wpisów zaproponowanej przez Renię pojawił się poważny problem wynikający z faktu, że data zapisana jako łańcuch znaków ma niewiele wspólnego z pojęciem czasu. Innymi słowy, nie ma jak porównać łańcuchów znaków ”14/08/2008” i ”19/08/2008”, by przekonać się, który z nich jest bardziej aktualny — w końcu są to tylko łańcuchy znaków. Oczywiście można porównywać łańcuchy znaków, jednak nie będzie brany pod uwagę specyficzny format, w jakim zapisywane są daty, więc wyróżnienie i porównanie liczby dni, miesięcy i lat nie będzie możliwe. A zatem, zanim zaczniemy poważnie myśleć o sortowaniu wpisów w blogu Reni, musimy poważnie zastanowić się nad sposobem przechowywania dat.

jesteś tutaj  423

Czy widziałeś już moje daty?

Obiekt daty w JavaScripcie Tym, na czym obecnie najbardziej zależy Reni, jest możliwość przechowywania dat w taki sposób, by można je było między sobą porównywać. Innymi słowy, data musi wiedzieć, że jest datą, i stosownie do tego się zachowywać. Chwila… z tego wynika, że data powinna być obiektem! Bo właśnie tak jest, a co więcej, okazuje się, że JavaScript udostępnia wbudowany obiekt, który doskonale spełni wymagania Reni. Data pierwszego wpisu w blogu.

Date setMonth() Metody ustawiające informacje o dacie.

getDate() 14 sierpnia 2008

setYear()

Metody zwracające informacje o dacie.

getDay()

getFullYear()

Wbudowany obiekt Date reprezentuje wybrany moment w czasie.

Obiekt Date reprezentuje moment w czasie (z dokładnością do milisekundy). Jest to jeden z wielu wbudowanych obiektów dostępnych w języku JavaScript. Choć bez wątpienia wewnątrz niego są używane jakieś właściwości, to jednak dla programistów, którzy go używają, są one niewidoczne. Zatem wszelkie operacje na obiektach Date są wykonywane przy użyciu metod. Obiekt Date, podobnie jak nasz niestandardowy obiekt Blog, tworzy się przy użyciu operatora new. Poniżej przedstawiliśmy przykład pokazujący, w jaki sposób można utworzyć obiekt Date, reprezentujący aktualny dzień i godzinę:

ny obiekt var teraz = new Date(); Nowo utworzo my je Date zapisu Obiekt Date tworzymy w zmiennej. przy użyciu operatora new.

Czas przechowywany wewnątrz obiektu Date

Przedstawiony powyżej obiekt Date zostaje zainicjowany informacjami o aktualnej dacie i godzinie. Zwróć uwagę, że składnia używana do utworzenia tego obiektu bardzo przypomina wywołanie funkcji lub metody — nie jest to przypadek, gdyż w rzeczywistości wywołujemy konstruktor obiektu. W wywołaniu konstruktora Date() można przekazać łańcuch znaków określający dowolną datę — inną niż aktualna. Na przykład przedstawiony poniżej obiekt reprezentuje datę pierwszego wpisu w blogu Reni1: var blogDate = new Date(”08/14/2008”);

jest wyrażony w milisekundach.

424

Rozdział 9.

Ten obiekt Date reprezentuje aktualną datę i godzinę.

1

Data jest przekazywana do konstruktora w postaci łańcucha znaków.

W związku z tym, że konstruktor obiektu Date, przyjmujący w wywołaniu ciąg znaków, wykorzystuje metodę parse() obiektu Date w celu odczytania z ciągu poszczególnych elementów daty, ciąg ten powinien mieć określony format. Niestety format popularny w Polsce: DD/MM/RRRR nie jest poprawnie rozpoznawany przez tę metodę, dlatego przy konstrukcji obiektów Date będziemy wykorzystywać format MM/DD/RRRR."

Ożywianie danych

Wyliczanie czasu Jednym z najważniejszych aspektów obiektów jest to, że same doskonale wiedzą, co należy robić z zapisanymi w nich informacjami. Zastanów się na przykład, jak złożone byłoby samodzielne wyliczenie liczby dni pomiędzy dwiema datami. W tym celu musiałbyś w jakiś sposób przeliczyć datę na liczbę dni od jakiegoś określonego momentu czasu (uwzględniając przy tym lata przestępne). A przecież wystarczy pozwolić, by to obiekt Date wykonał za nas całą „brudną” robotę… Zresztą sam spójrz na kod poniższej funkcji obliczającej liczbę dni między dwiema podanymi datami: Funkcja wymaga przekazania dwóch obiektów Date.

Wyznaczoną różnicę między data mi konwertujemy najpierw z milisekun d na sekundy, następnie minuty, godziny, a w końcu na dni. O rany !

function dniMiedzyDatami(data1, data2) { var liczbaDni = (data2 – data1) / (1000 * 60 * 60 * 24); return Math.round(liczbaDni); } Wynik zaokrąglamy i zwracamy; round() jest metodą obiektu Mat h, który dokładniej przedstawimy w dalszej części tego rozdziału.

dniMiedzyDatami(data1, data2);

ćwiczenie

Prosty, lecz potężny kod, który robi to, o co nam chodziło.

Powyższa funkcja ukazuje możliwości obiektu Date, a wszystko dzięki zastosowaniu jednego prostego działania — odejmowania. Cała złożoność zagadnienia, a konkretnie wyliczania różnicy pomiędzy datami, jest w bardzo wygodny sposób ukryta wewnątrz obiektu Date. W powyższym przykładzie pozostaje nam jedynie zająć się odpowiednim przetworzeniem uzyskanego wyniku, którym jest różnica pomiędzy dwiema datami wyrażona w milisekundach. Wystarczy zatem przeliczyć milisekundy na dni, zaokrąglić wynik i uzyskamy bardzo wygodną funkcję, której będzie można wielokrotnie używać, gdy potrzebna nam będzie różnica pomiędzy dwiema datami.

Utwórz dwa obiekty Date dla dat pierwszych dwóch wpisów w blogu Reni. Następnie wywołaj funkcję dniMiedzyDatami(), przekazując do niej utworzone wcześniej daty, i w końcu wyświetl uzyskany wynik w informacyjnym okienku dialogowym. ................................................................................................................................................................................ ................................................................................................................................................................................

jesteś tutaj  425

Rozwiązanie ćwiczenia

ćwiczenie Rozwiązanie

Utwórz dwa obiekty Date dla dat pierwszych dwóch wpisów w blogu Reni. Następnie wywołaj funkcję dniMiedzyDatami(), przekazując do niej utworzone wcześniej daty, i w końcu wyświetl uzyskany wynik w informacyjnym okienku dialogowym.

Tworzymy obiekty Date dla dat dwóch pierwszych wpisów w blogu Reni.

var................................................................................................................................................................................ dataBlogu1 = new Date("08/14/2008"); var................................................................................................................................................................................ dataBlogu2 = new Date("08/19/2008"); alert("Różnica między podanymi datami wnosi " + dniMiedzyDatami(dataBlogu1, dataBlogu2) + " dni."); ................................................................................................................................................................................

Funkcja zwraca różnicę.

Jako argumenty wywołania funkcji przekazujemy dwa obiekty Date.

Ponowna analiza zagadnienia dat w blogu To naprawdę super, że JavaScript udostępnia wbudowany obiekt Date, pozwalający na wykonywanie inteligentnych operacji na datach. Szkoda tylko, że na stronie MagicznaKostka obiekty Blog wciąż przechowują daty w formie łańcuchów znaków, a nie obiektów Date. Jeśli chcemy skorzystać z możliwości, jakie udostępniają obiekty Date, musimy zmodyfikować kod strony tak, by daty były przechowywane jako obiekty Date.

Date

Blog "14/08/2008" "Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko."

Wartość właściwości date obiektu Blog musimy przekształcić z łańcucha znaków do postaci obiektu Date.

14 sierpnia, 2008

Oto pytanie, na które musimy znaleźć odpowiedź: „Czy we właściwości date obiektu Blog zamiast łańcucha znaków można zapisać obiekt Date?”.

426

Rozdział 9.

Ożywianie danych

Obiekt w obiekcie Obiekt Blog jest doskonałym przykładem pokazującym, że czasami obiekty muszą zawierać inne obiekty. Obie właściwości obiektu Blog same są obiektami, a konkretnie — obiektami String. Obiekty String może nie wyglądają jak obiekty, gdyż są tworzone przy użyciu literałów obiektowych poprzez podanie tekstu zapisanego w cudzysłowach. Obiekty Date nie są aż tak elastyczne i trzeba je tworzyć przy wykorzystaniu operatora new. Aby we właściwości date wpisu w blogu zapisać datę w postaci obiektu, musimy utworzyć nowy obiekt Date podczas tworzenia obiektu Blog. Jeśli sądzisz, że zadanie to jest czymś koszmarnym, to może niewielki przykład uspokoi Twoje obawy:

Literał łańcuchowy automatycznie tworzy obiekt String.

Obiekt Blog jest tworzo przy użyciu operatora ny new.

var blogEntry = new Blog("Nic się nie dzieje, tylko pogoda jest koszmarna...", new Date("10/31/2008")); jemy Tworzymy obiekt Date i przekazu (); go w wywołaniu konstruktora Blog także w tym przypadku używamy operatora new.

Powyższy kod pokazuje, jak obecnie tworzymy wpisy w blogu na stronie MagicznaKostka. Wpisy te są obiektami zawierającymi dwie właściwości (w pierwszej z nich zapisywany jest obiekt String, a w drugiej obiekt Date). Oczywiście, wciąż musimy utworzyć tablicę obiektów Blog, aby wyświetlić na stronie MagicznaKostka wszystkie wpisy. Właściwość body

Blog

String

"Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko."

Date

14 sierpnia 2008

Operator new tworzy obiekt, korzystając przy tym z pomocy konstruktora.

"Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko." 14/08/2008

Właściwość date

Zaostrz ołówek

Zmodyfikuj kod aplikacji MagicznaKostka, tak by tworzył tablicę obiektów Blog, z których każdy będzie zawierał datę utworzoną jako obiekt Date. Możesz uprościć sobie życie i zapisać tylko kilka początkowych słów z treści poszczególnych wpisów.

.................................................................................................................................................................... .................................................................................................................................................................... .................................................................................................................................................................... ....................................................................................................................................................................

jesteś tutaj  427

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie Każdy wpis wciąż jest tworzony jako obiekt Blog.

Zmodyfikuj kod aplikacji MagicznaKostka, tak by tworzył tablicę obiektów Blog, z których każdy będzie zawierał datę utworzoną jako obiekt Date. Możesz uprościć sobie życie i zapisać tylko kilka początkowych słów z treści poszczególnych wpisów.

var blog = [new Blog("Dostałam właśnie nową,…", new Date("08/14/2008")), .................................................................................................................................................................... new Blog("Udało mi się już…", new Date("08/19/2008")), .................................................................................................................................................................... new Blog("Już mnie głowa rozbolała…", new Date("08/16/2008")), .................................................................................................................................................................... new Blog("Znalazłam w internecie…", new Date("08/21/2008")) ]; .................................................................................................................................................................... Literały łańcuchowe doskonale nadają się do podania treści wpisów.

Data każdego wpisu tworzona jest jako nowy obiekt Date.

Nie ma

niemądrych pytań

P: Dlaczego data w obiekcie Date jest

przechowywana w postaci milisekund?

O: Przede wszystkim musisz zrozumieć, że obiekt Date

reprezentuje pewien moment czasu. Gdyby wszechświat posiadał przycisk Pauza i gdybyś mógł go nacisnąć, to zatrzymałbyś wszechświat w pewnym określonym momencie czasu. Jednak bez jakiegoś punktu odniesienia nie byłbyś w stanie powiedzieć innym osobom, kiedy ten moment nastąpił. Dlatego też jako punkt odniesienia w czasie wybrałeś dzień 1 stycznia 1970 roku. Teraz potrzebujesz jeszcze jakiejś miary do określenia przesunięcia względem tego punktu odniesienia, na przykład 38 lat, 8 miesięcy, 14 dni, 3 godziny, 29 minut i 11 sekund. Byłby to jednak dziwny sposób przechowywania informacji o odległości dwóch punktów

na osi czasu. Lepiej będzie zastosować tylko jedną jednostkę miary, za pomocą której będziemy w stanie przedstawić nawet najkrótsze fragmenty czasu. A zatem zamiast tych wszystkich jednostek czasu mam jedną — milisekundy. W przypadku podanego wcześniej okresu jego wartość w milisekundach wyniosłaby 1 218 702 551 000. No tak… to cała masa milisekund, jednak duże liczby nie przysparzają JavaScriptowi równie dużych problemów.

P: Czy stosując obiekt Date, muszę się przejmować dokonywaniem konwersji na milisekundy?

O: To zależy. Obiekt Date udostępnia kilka metod pozwalających na pobieranie przydatnych informacji bez konieczności zawracania sobie głowy milisekundami. Jeśli jednak będziesz musiał skorzystać z różnic między datami, to niemal na pewno zetkniesz się z milisekundami.

Kluczowe zagadnienia 

Standardowy obiekt Date JavaScriptu reprezentuje moment czasu wyrażony w milisekundach.



Obiekt Date jest na tyle mądry, że wie, jak wykonywać matematyczne operacje na datach oraz jak porównywać daty.



Obiekt Date udostępnia kilka metod pozwalających na pobieranie różnych informacji o dacie i godzinie.



Podobnie jak w przypadku wszystkich wcześniejszych obiektów, za wyjątkiem obiektów String, obiekty Date także tworzy się przy użyciu operatora new.

428

Rozdział 9.

Ożywianie danych

Daty nie są przydatne… dla ludzi A zatem po skonwertowaniu dat we wpisach do swojego blogu na obiekty Date Renia jest już gotowa, by zająć się problemem sortowania wpisów na podstawie daty. Cóż… jest prawie gotowa. Okazuje się bowiem, że wprowadzone zmiany spowodowały, iż daty wyświetlane na stronie MagicznaKostka wyglądają bardzo tajemniczo i nieczytelnie. Renia przypuszcza, że osoby oglądające jej blog nie będą zainteresowane informacją o tym, w jakiej strefie czasowej były tworzone poszczególne wpisy, a takie niepotrzebne informacje jedynie utrudnią korzystanie ze strony i sprawią, że stanie się ona mniej czytelna. Okazuje się, że znacznie dokładniej musimy przyjrzeć się efektom zastosowania obiektów Date na stronie blogu Reni. Daty wpisów stały się bardzo tajemnicze i zagmatwane… Zawierają zbyt dużo informacji!

Zastosowanie obiektów Date może i było sensownym rozwiązaniem, jednak teraz daty wyświetlane na stronie wyglądają koszmarnie. A nie przypominam sobie, żebym pisała jakiś kod służący do formatowania dat.

Daty nie tylko wyglądają kiepsko, lecz co gorsza, wpisy w blogu wciąż są wyświetlane w niewłaściwej kolejności — dramat!

Renia jest nieco zaskoczona nowym problemem związanym ze sposobem wyświetlania dat; jest on tym bardziej tajemniczy, że Renia nie przypomina sobie, by pisała jakikolwiek kod służący do prezentowania nowych dat. Jedyną zmianą, jaką wprowadziła, było skonwertowanie łańcuchów znaków na obiekty Date. Czy to jakieś ciemne moce JavaScriptu zmówiły się, by popsuć sposób prezentacji dat na jej stronie?

jesteś tutaj  429

Musi być jakiś lepszy sposób

Konwersja obiektów na łańcuchy znaków Na szczęście za nieatrakcyjnym sposobem wyświetlania dat w nowej wersji strony MagicznaKostka nie stoją żadne ciemne moce. W rzeczywistości jest to przejaw całkiem naturalnej mocy wszystkich obiektów w języku JavaScript — mocy do wyświetlania swojej zawartości w postaci łańcucha znaków. Mechanizm ten działa w następujący sposób: każdy obiekt JavaScriptu posiada metodę toString(), która próbuje wygenerować tekstową reprezentację obiektu. A zatem za tym zagmatwanym sposobem wyświetlania daty stoi domyślna implementacja metody toString() obiektu Date. var blogDate = new Date(”08/14/2008”); alert(blogDate.toString());

Najciekawszym aspektem metody toString() jest to, że jest ona używana automatycznie, w przypadku gdy obiekt zostanie użyty w kontekście, w którym oczekiwano podania łańcucha znaków. Na przykład przedstawiony powyżej kod wyświetlający okienko dialogowe z datą można by zmienić w następujący sposób:

Metoda toString() pokazuje również, w jaki sposób obiekt Date przechowuje także informacje o czasie.

Date

alert(blogDate);

Funkcja alert() oczekuje przekaza łańcucha znaków, a zatem w ukrynia sposób jest wywoływana metoda ty toString(), która generuje tekstową reprezentację obiektu Date.

Metoda toString() generuje tekstową reprezentację obiektu.

430

Rozdział 9.

toString()

Metoda toString() całkiem dobrze radzi sobie z zadaniem dostarczenia tekstowej reprezentacji obiektu Date, jednak jest ona dostępna także w innych obiektach.

14 sierpnia 2008 Północ

Funkcja alert() oczekuje łańcucha znaków, a obiekt Date jest na tyle sprytny, by wiedzieć, że musi wygenerować swoją tekstową reprezentację. Zadanie to jest realizowane właśnie poprzez wywołanie metody toString(). Całe to zamieszanie z metodą toString() nie byłoby większym problemem, gdyby nie to, że strona MagicznaKostka naprawdę wymaga, by daty były wyświetlane w formacie łatwym do zrozumienia dla oglądających ją osób, na przykład w formacie DD/MM/RRRR. Wniosek: wygląda na to, że Renia nie będzie mogła wykorzystać na swojej stronie domyślnej reprezentacji tekstowej obiektów Date, generowanych przez metodę toString().

Ożywianie danych

Pobieranie konkretnych informacji o dacie Renia potrzebuje sposobu, który pozwoliłby jej określić, jak należy wyświetlać daty. Dla zapewnienia takiej możliwości kluczowe znaczenie ma dostęp do szczegółowych informacji o dacie, takich jak numer dnia czy miesiąca bądź rok. Dysponując tymi danymi, będziemy w stanie wyświetlić je w dowolny sposób. Na szczęście obiekt Date udostępnia metody zwracające szczegółowe informacje na temat daty.

Date

14 getDate()

14 sierpnia 2008 Północ

getMonth()

7 2008

Dzień miesiąca zapisany jako liczba z zakresu od 1 do 31.

Miesiąc zapisany jako liczba z zakresu od 0 do 11.

Pełny, czterocyfrowy format zapisu roku.

getFullYear()

W rzeczywistości obiekt Date udostępnia znacznie więcej niż trzy powyższe metody, zapewniając tym samym wiele różnych sposobów pobierania daty i czasu. Jednak trzy przedstawione powyżej metody to wszystko, czego nam potrzeba, by wyświetlać daty na stronie MagicznaKostka w taki sposób, jaki będzie nam odpowiadać.

Oglądaj to!

Zwróć szczególną uwagę na wartości zwracane przez metody obiektu Date.

iąca Metoda getMonth() zwraca numer mies zeń) do wyrażony jako liczba z zakresu od 0 (styc ca zwra ) 11 (grudzień); z kolei metoda getDate( 31. do numer dnia jako liczbę z zakresu od 1

Zaostrz ołówek Rozwiąż problem dziwacznego formatu, w jakim aktualnie wyświetlane są daty na stronie MagicznaKostka. W tym celu przepisz kod, który określa postać wpisu i zapisuje wyniki w zmiennej blogText. Upewnij się, że data zostanie zapisana w formacie DD/MM/RRRR. Poniżej przedstawiliśmy oryginalną postać tego kodu: blogText += "" + blog[i].date + "
"+ blog[i].body + "

";

.................................................................................................................................................................... .................................................................................................................................................................... .................................................................................................................................................................... ....................................................................................................................................................................

jesteś tutaj  431

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie

Rozwiąż problem dziwacznego formatu, w jakim aktualnie wyświetlane są daty na stronie MagicznaKostka. W tym celu przepisz kod, który określa postać wpisu i zapisuje wyniki w zmiennej blogText. Upewnij się, że data zostanie zapisana w formacie DD/MM/RRRR. Poniżej przedstawiliśmy oryginalną postać tego kodu:

blogText += "" + blog[i].date + "
"+ blog[i].body + "

"; blogText += "" + blog[i].date.getDate() + "/" + ........................................................................................................................................................... Dzięki temu, że nie korzystamy z domyślnego sposobu formatowania daty, uzyskujemy większą kontrolę nad jej postacią.

(blog[i].date.getMonth() + 1) + "/" + ........................................................................................................................................................... blog[i].date.getFullYear() + "

" + ........................................................................................................................................................... blog[i].body + "

"; ........................................................................................................................................................... Każda informacja o dacie jest pobierana z obiektu Date poprzez wywołanie odpowiedniej metody.

Obecnie data wpisu wyświetlana na stronie prezentowana jest w niestandardowym formacie DD/MM/RRRR.

Daty ułatwiają sortowanie A zatem skoro udało się nam skonwertować daty wpisów na obiekty Date, które znacznie lepiej nadają się do sortowania niż łańcuchy znaków, nadszedł czas, by wyświetlić wpisy w blogu posortowane w odpowiedniej kolejności. Problem Reni polega na tym, że wpisy na stronie są wyświetlane w takiej samej kolejności, w jakiej są zapisywane w tablicy blog, która niekoniecznie jest kolejnością chronologiczną. Natomiast w przeważającej większości przypadków wpisy w blogach są wyświetlane w odwrotnej kolejności chronologicznej, czyli najnowszy wpis jest wyświetlany jako pierwszy. Wiedząc już, co chcemy osiągnąć, możemy zmienić początkową strategię sortowania wpisów w następujący sposób:

Date

14 sierpnia 2008

Date

< 19 sierpnia 2008

Teraz używamy już obiektów Date, które można ze sobą porównywać.

1

Przeglądamy w pętli tablicę wpisów.

2

Porównujemy obiekt Date z bieżącego obiektu Blog z datą z następnego wpisu.

3

Jeśli data z następnego wpisu jest nowsza od daty z aktualnego wpisu, to oba wpisy zamieniamy miejscami. Choć w naszej sytuacji porównywanie dat wydaje się znacznie mniej przerażające, to jednak cała pozostała część algorytmu i tak wymaga stosunkowo dużo pracy. Sortowanie sekwencji dat wydaje się być takim problemem programistycznym, który w przeszłości został już rozwiązany wiele razy. A przecież nikt nie lubi ponownie wymyślać koła…

432

Rozdział 9.

Ożywianie danych

Czyż nie byłoby cudownie, gdyby JavaScript posiadał jakiś wbudowany mechanizm sortowania, który ukryłby przed nami całą złożoność operacji sortowania sekwencji danych?

jesteś tutaj  433

Sortowanie siebie samego

Tablice jak obiekty Czy to możliwe, by tablica była w stanie sama posortować swoją zawartość? Skoro data wie, w jaki sposób wyrazić się w postaci łańcucha znaków, to niewiele więcej trzeba, by wyobrazić sobie, że tablica może sama się posortować. Aby to w ogóle było możliwe, tablica musiałaby być obiektem, żeby operacja sortowania była wykonywana przez jakąś metodę. Okazuje się, że faktycznie tak jest. Czy pamiętasz poniższy fragment kodu zastosowany w aplikacji Mandango?

Tak naprawdę tablica jest obiektem.

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

Właściwość obiektu tablicy length umożliwia odczytanie liczby elementów tej tablicy.

Zmienna seats jest tablicą.

A zatem karty na stół — tablice są obiektami, ale czy to jednocześnie oznacza, że tablice mogą się same sortować? Tablice nie tylko mają właściwości, takie jak length, lecz także dysponują metodami, które pozwalają im operować na przechowywanej zawartości i ożywiać ją. I tak się dziwnie składa, że faktycznie tablice posiadają metodę sort(), która pozwala posortować dane przechowywane w tablicy. Przekonajmy się, jak działa ta metoda:

liczby

51

34

11

0

1

2

17

29 3

4

5

58

22

46 6

7

16 8

Tablica liczb.

var liczby = [ 51, 11, 34, 29, 17, 46, 22, 58, 16 ]; liczby.sort();

To wywołanie sortuje tablicę w rosnącej kolejności.

sort()

Metoda sort() zmienia kolejność elementów zapisanych w tablicy. Jej domyślne działanie polega na posortowaniu zawartości tablicy w kolejności rosnącej; a zatem powyższe wywołanie metody sort() sprawi, że zawartość przykładowej tablicy będzie wyglądać w następujący sposób:

0 434

Rozdział 9.

17

16

11 1

2

29

22 3

4

34 5

6

46

51 7

58 8

Ożywianie danych

Sortowanie tablic wedle własnych potrzeb Niejednokrotnie domyślny sposób działania metody sort() obiektów Array nie zaspokoi naszych potrzeb. Na szczęście okazuje się, że sposób tego sortowania zależy od funkcji porównującej, używanej przez metodę sort() do porównywania sortowanych elementów tablicy. Możemy zatem dostosować sortowanie do własnych potrzeb, wystarczy w tym celu stworzyć własną wersję funkcji porównującej. Poniżej pokazaliśmy, jak taka funkcja zazwyczaj wygląda: Argumentami wywołania funkcji są elementy tablicy, które są porówny dwa wane w celu określenia ich właściwej kolejności.

function porownaj(x, y) { return x – y; }

Wartość wynikowa określa, czy wartości x i y pozostaną na swoich dotychczasowych miejscac h w tablicy, czy też wartość y powinna się znaleźć przed wartością x.

<0

Wartość wynikowa, zwrócona przez funkcję porownaj(), jest liczbą całkowitą, określającą ostateczną kolejność wartości x i y.

0

porownaj(x, y)

>0

stanie Wartość x zo ścią y. przed warto umieszczona

ści ania — warto Brak sortow na swoich x i y zostaną ch. wych miejsca dotychczaso

stanie Wartość y zo ścią x. przed warto umieszczona

Nasza niestandardowa funkcja porownaj() pojawia się na scenie sortowania w momencie wywoływania metody sort() — wystarczy przekazać referencję do tej funkcji jako argument wywołania metody sort(). liczby.sort(porownaj);

Teraz sposób sortowania tablicy jest określany przez naszą niestandardową funkcję porownaj().

Zaostrz ołówek Napisz kod własnej funkcji porównującej o nazwie compare(), która będzie sortować wpisy w blogu na stronie MagicznaKostka, porządkując je w odwrotnej kolejności chronologicznej (czyli najnowszy wpis ma być wyświetlony jako pierwszy). Podpowiedź: daty w obiektach Blog można odejmować od siebie, używając zwyczajnego operatora odejmowania (znaku minusa). .................................................................................................................................................................... .................................................................................................................................................................... ....................................................................................................................................................................

jesteś tutaj  435

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie

Napisz kod własnej funkcji porównującej o nazwie compare(), która będzie sortować wpisy w blogu na stronie MagicznaKostka, porządkując je w odwrotnej kolejności chronologicznej (czyli najnowszy wpis ma być wyświetlony jako pierwszy). Podpowiedź: daty w obiektach Blog można odejmować od siebie, używając zwyczajnego operatora odejmowania (znaku minusa).

Oba argumenty są obiektami Blog, gdyż sortowana function compare(blog1, blog2) { .................................................................................................................................................................... tablica zawiera return blog2.date – blog1.date; właśnie takie .................................................................................................................................................................... obiekty. }

.................................................................................................................................................................... od Odjęcie daty pierwszego wpisu ik daty drugiego wpisu zwróci wyn zgodny z odwrotną kolejnością chronologiczną.

Odejmujemy od siebie dwie daty zapisane w postaci liczby (określającej liczbę milisekund).

Łatwiejsze sortowanie dzięki literałom funkcyjnym Kiedy zastanowisz się dokładniej nad rolą i znaczeniem funkcji porównującej, to okaże się, że jest ona używana wyłącznie przez metodę sort(). Ponieważ żaden inny kod umieszczony na stronie MagicznaKostka nie korzysta z tej funkcji, zatem nie ma szczególnego sensu, by tworzyć ją jako funkcję o określonej nazwie. Czy pamiętasz jeszcze literały funkcyjne, które przedstawiliśmy w rozdziale 6.? Funkcja compare(), którą napisałeś w ostatnim ćwiczeniu, jest doskonałą kandydatką na literał funkcyjny. Co więcej, dzięki zaimplementowaniu jej w postaci literału cały kod odpowiedzialny za sortowanie blogu na stronie Reni można zdecydowanie uprościć. blog.sort(function(blog1, blog2) { return blog2.date – blog1.date;

Nasz nowy literał funkcyjny zost anie umieszczony bezpośrednio w wyw metody sort() sortującej tablicę. ołaniu

});

Jako doświadczona miłośniczka układanek Renia bardzo sobie ceni efektywność. A w naszym przypadku efektywność sprowadza się do usunięcia niepotrzebnej nazwanej funkcji, która i tak jedynie pomaga metodzie sort(). Renia jest tak wyczulona na punkcie wydajności, że nie podoba się jej nawet to, że funkcja porównująca została zapisana w trzech wierszach kodu. Choć organizacja kodu JavaScript nie ma wpływu na szybkość jego działania, to jednak w tym przypadku literał funkcyjny jest na tyle prosty, że zapisanie go w jednym wierszu kodu ma sens. blog.sort(function(blog1,blog2) { return blog2.date – blog1.date;});

436

Rozdział 9.

Literał funkcyjny został zapisany w jednym wierszu kodu.

Ożywianie danych

Nie ma

niemądrych pytań

P: Czy każdy obiekt ma metodę

P: W jaki sposób działa

O: Tak. Nawet jeśli utworzysz własny

O: Przeznaczeniem funkcji porównującej

toString()?

obiekt i nie zaimplementujesz w nim metody toString(). Jeśli jednak użyjesz takiego obiektu w kontekście, w którym wymagany był łańcuch znaków, to JavaScript przynajmniej poinformuje cię o tym fakcie. Domyślna wersja metody toString() nie będzie generować żadnych opisowych informacji, pamiętaj zatem, że jeśli chcesz, by metoda toString() udostępniała przydatne informacje na temat twojego obiektu, to powinieneś zaimplementować ją sam.

porównywanie obiektów Date?

jest zwrócenie liczby całkowitej, kontrolującej proces sortowania dwóch wartości. W naszym przykładzie sortowania dat chcemy, by późniejsza data znalazła się przed datą wcześniejszą. Im późniejsza data, tym jej wartość jest większa, zatem odjęcie pierwszej daty od drugiej sprawi, że w wynikach sortowania późniejsza data znajdzie się przed datą wcześniejszą. Oznacza to, że druga data zostanie umieszczona przed pierwszą jedynie w przypadku, gdy ta druga data jest większa (czyli funkcja porównująca zwróci wynik większy od 0).

P

: Skąd metoda Array.sort() ma wiedzieć, czy należy użyć niestandardowej funkcji porównującej, czy domyślnego sposobu porównywania?

O: Metoda sort() może to określić na

podstawie tego, czy w jej wywołaniu został przekazany jakiś argument, czy nie. Jeśli żadnego argumentu nie będzie, metoda użyje domyślnego sposobu porównywania; w przeciwnym razie, jeśli argument będzie, metoda potraktuje go jako odwołanie do funkcji i użyje jako podstawy do określania kolejności sortowanych elementów. A zatem referencja do funkcji porównującej jest argumentem opcjonalnym.

Renia i jej sześcienne układanki są zadowolone Lista wpisów w końcu odpowiada wyobrażeniom Reni co do tego, jak powinien wyglądać blog prawdziwej miłośniczki układanek, która chce podzielić się swoimi myślami z całym wszechświatem.

Daty są zrozumiałe i ładnie wyglądają.

Kocham swój blog niemal tak samo jak swoje układanki.

Poszczególne wpisy są uporządkowane od najnowszego do najstarszego.

jesteś tutaj  437

Zagadka wyszukiwania

Wyszukiwanie byłoby miłe Strona MagicznaKostka działa doskonale, jednak kilku użytkowników zasugerowało, że przydałaby się im możliwość wyszukiwania, pozwalająca odnaleźć wszystkie wpisy zawierające podany łańcuch znaków. Ponieważ Renia zakłada, że kiedyś jej blog będzie zawierać bardzo dużo wpisów, zatem uznała, iż możliwość wyszukiwania faktycznie się przyda… zwłaszcza w dłuższej perspektywie. Użytkownik wpisuje łańcuch znak który następnie zostanie użyty ów, do przeszukania treści wpisów w blog u.

onclick!

......................

.............................................

.............................................

............................... .............................................

14/08/2008 ankę....................... układ......... .................. , zamó .........wioną ......... Dostałam właśnie nową ............................... ......... ......... ko. ......... cudeń ......... ziwe......... prawd......... To ......... ..................

....................................

.............................................

Ten mechanizm pozwoli użytkownikom przeszukać wszystkie wpisy w moim blogu i odnaleźć te, które zawierają podany tekst — wystarczy tylko wpisać tekst i kliknąć przycisk.

......................

.............................................

.............................................

...................... .............................................

19/08/2008 mnie ............. Już ......... ankę.......... układ ......... nową......... ć tę......... ......... Udało mi się już ułoży ...................... go. ......... nowe ......... ś ......... czego m ......... szuka ......... więc......... ziła,......... znud ......... ..................

.............................................

.............................................

......................

.............................................

.............................................

...................... .............................................

16/08/2008 ą. ............. kostk ......... nową ......... ......... od zabaw .........tą lała ......... ......... Już mnie głowa rozbo ...................... ......... ......... ......... ......... ząć. odpoc .................. ę sobie ......... Musz ......... ..................

.............................................

.............................................

......................

.............................................

.............................................

.............

......... ............................................. 21/08/2008 rany! O 7. ...................... 7×7× ......... ankę ......... ecie układ .................. intern......... w ......... złam ......... Znala ......... .................. ...................... ......... ......... ......... . ......... super......... będzie......... wa ......... Zaba ......... ..................

.............................................

......................

.............................................

.............................................

...................... 29/08/2008 ............................................. i, eńcam zapal i ...................... innym ......... ma kilko ......... Spotkałam się z ........................... ............................................. 7. 7×7× i kostk ...................... aniu ......... układ o ......... ać ......... mawi by poroz .................. ............................................. cia. uczu ane miesz ............................... mam iąc, ......... ......... ......... I szczerze mów ......... .........

.............................................

....................................

Renia potrzebuje teraz planu, który określi, jak ma działać mechanizm przeszukiwania blogu… Czy obiekty odegrają w nim jakąś rolę?

438

Rozdział 9.

Ożywianie danych

Przeszukiwanie wpisów w blogu Mechanizm wyszukiwania na stronie MagicznaKostka wymaga przeanalizowania w pętli wszystkich wpisów w blogu i sprawdzenia, czy znajduje się w nich poszukiwany tekst.

Pobranie poszukiwanego łańcucha znaków podanego przez użytkownika.

Przejrzenie w pętli wszystkich wpisów blogu.

Ten projekt jest sensowny, ale niby w jaki sposób mam wyszukać tekst w treści wpisu? Na razie jestem w kropce, ale wiem, że musi być jakiś sposób!

Sprawdzenie, czy analizowany wpis zawiera poszukiwany tekst.

Zakończenie pętli, jeśli tekst został odnaleziony.

WYTĘŻ umysł

Jak można sprawdzić, czy treść wpisu zawiera w sobie poszukiwany łańcuch znaków?

jesteś tutaj  439

Odpowiedzią są łańcuchy

Już wiemy, że łańcuchy znaków są obiektami. Czy zatem jest możliwe, żeby te obiekty mogły same się przeszukiwać?

Łańcuch to obiekt, który potrafi przeszukiwać swoją zawartość. Może już zaczynasz łapać, że w JavaScripcie obiekty są wszędzie. Łańcuchy znaków są obiektami, a konkretnie obiektami String. Obiekty te udostępniają wiele metod służących do operowania na zawartości łańcucha (czyli na tekście). I miałeś rację — jedna z tych metod pozwala na wyszukanie fragmentu tekstu w łańcuchu. Czasami taki fragment łańcucha znaków określany jest jako podłańcuch.

Sprawdza, czy łańcuch zawiera podany ciąg znaków.

String indexOf()

length

"Dostałam właśnie charAt() nową, zamówioną układankę. To prawdziwe toLowerCase() cudeńko."

toUpperCase() Liczba znaków w łańcuchu.

440

Rozdział 9.

Zwraca znak znajdujący się we wskazanym miejscu łańcucha.

Przekształcają łańcuch znaków tak, aby był w całości zapisany małymi lub wielkimi literami.

Ożywianie danych

Przeszukiwanie zawartości łańcucha znaków indexOf() Metoda indexOf() pozwala sprawdzić, czy w obiekcie String występuje podany ciąg znaków. Poszukiwany fragment łańcucha jest przekazywany jako argument wywołania metody; a ponieważ metoda ta zawsze jest wywoływana na rzecz jakiegoś obiektu String, zatem nie ma potrzeby przekazywania do niej jakichkolwiek dodatkowych argumentów. Metoda indexOf() zwraca indeks określający położenie odnalezionego fragmentu łańcucha; jeśli poszukiwany ciąg znaków nie zostanie odnaleziony, metoda zwróci wartość –1. var str = ”Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko.”; alert(str.indexOf(”nową”));

Aby zrozumieć, dlaczego w powyższym przykładzie wyświetlana jest liczba 17, musisz wyobrazić sobie, że łańcuch jest tablicą pojedynczych znaków.

0

10

20

30

40

Każdy znak w łańcuchu jest umieszczony w innym miejscu, którego położenie określa indeks. Wartości tego indeksu są liczone od 0, przy czym indeks 0 wskazuje na pierwszy znak łańcucha.

"Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko." Szukany ciąg znaków "nową" poja się w całym łańcuchu na miejscu wia o indeksie 17.

Jeśli używając metody indexOf(), spróbujemy odszukać ciąg znaków, który nie występuje w łańcuchu, to metoda ta zwróci wartość -1. var indeksWystapienia = str.indexOf("eklerka");

ćwiczenie

To wywołanie zwróci w wyniku wartość –1, gdyż podany tekst nie występuje w danym obiekcie String.

Poniżej jedno ze zdań zasłyszanych przez Renię. Twoim zadaniem jest podanie indeksu każdego wystąpienia ciągu "kost".

"Układanie kostki w kosmosie jest marzeniem każdego kostkowego maniaka."

jesteś tutaj  441

Rozwiązanie ćwiczenia

Poniżej jedno ze zdań zasłyszanych przez Renię. Twoim zadaniem jest podanie indeksu każdego wystąpienia ciągu "kost".

ćwiczenie Rozwiązanie

Początek łańcucha znaków ma indeks 0.

"Układanie kostki w kosmosie jest marzeniem każdego kostkowego maniaka." Indeks odnalezionego słowa ma wartość 10.

Indeks odnalezionego słowa ma wartość 51.

Przeszukiwanie tablicy blogu Dzięki metodzie indexOf() obiektu String przeszukiwanie łańcuchów znaków nie jest trudnym zadaniem, niemniej Renia i tak musi przeszukać wszystkie swoje wpisy. Jej plan zakłada analizowanie w pętli każdego wpisu i sprawdzanie przy użyciu metody indexOf(), czy w jego treści nie występuje poszukiwany łańcuch znaków. Jeśli łańcuch zostanie odnaleziony, to Renia zamierza wyświetlić wpis w informacyjnym okienku dialogowym. Zanim zajmiemy się napisaniem kodu funkcji, która będzie przeszukiwać blog na stronie MagicznaKostka, musimy na niej umieścić pole tekstowe służące do podania poszukiwanego tekstu oraz przycisk, którego kliknięcie rozpocznie wyszukiwanie.

ącego Wygodny dostęp do pola zawieraj wnia wyszukiwany łańcuch znaków zape identyfikator searchtext.

Szukany tekst!

Kliknięcie przycisku Przeszukaj blog powoduje wywołanie funkcji searchBlog(), która przeszukuje treści wszystkich wpisów w blogu.

Po umieszczeniu na stronie elementów HTML pozwalających na podanie poszukiwanego łańcucha znaków i rozpoczęcie wyszukiwania w końcu nadszedł czas, by napisać kod funkcji searchBlog(). Ponieważ funkcja ta będzie prezentować odnalezione wpisy w informacyjnym okienku dialogowym, zatem nie musi zwracać żadnych informacji. Funkcja ta nie wymaga także żadnych argumentów, gdyż będzie odczytywać poszukiwany łańcuch znaków bezpośrednio z pola tekstowego.

442

Rozdział 9.

Ożywianie danych

Magnesiki z JavaScriptem Funkcja searchBlog() jest odpowiedzialna za odczytanie w pętli wszystkich wpisów w blogu Reni i sprawdzenie, czy w ich treści występuje poszukiwany łańcuch znaków. Pomóż Reni dokończyć pisanie tej funkcji — uzupełnij brakujące miejsca magnesikami z kodem. Podpowiedź: dla każdego z odszukanych wpisów należy wyświetlić jego datę w formacie DD/MM/RRRR, zapisaną w nawiasach klamrowych, oraz treść wpisu.

// Przegląda listę wpisów w poszukiwaniu podanego fragmentu tekstu function searchBlog() { var

= document.getElementById("

for (var i = 0; i <

").value;

; i++) {

// Sprawdzamy, czy wpis zawiera poszukiwany tekst if (blog[i].

.toLowerCase().indexOf(searchText.toLowerCase()) != -1) {

alert("[" + blog[i].date.getDate() + "/" + (blog[i]. blog[i].

.getFullYear() + "] " + blog[i].

.

+

) + "/" +

);

break; } } // Jeśli nie udało się odnaleźć poszukiwanego tekstu, wyświetlamy komunikat if (i ==

)

alert("Przykro mi, ale żaden wpis nie zawierał poszukiwanego tekstu."); }

searchText

date

blog.length

1

searchtext

body

getMonth()

jesteś tutaj  443

Rozwiązanie ćwiczenia z magnesikami

Magnesiki z JavaScriptem — Rozwiązanie Funkcja searchBlog() jest odpowiedzialna za odczytanie w pętli wszystkich wpisów w blogu Reni i sprawdzenie, czy w ich treści występuje poszukiwany łańcuch znaków. Pomóż Reni dokończyć pisanie tej funkcji — uzupełnij brakujące miejsca magnesikami z kodem. Podpowiedź: dla każdego z odszukanych wpisów należy wyświetlić jego datę w formacie DD/MM/RRRR, zapisaną w nawiasach klamrowych, oraz treść wpisu. W pierwszej kolejności pobieramy poszukiwany łańcuch znaków z pola tekstowego.

tu tekstu // Przegląda listę wpisów w poszukiwaniu podanego fragmen function searchBlog() {

rchtext ").value; var searchText= document.getElementById(" sea for (var i = 0; i <

blog.length

; i++) {

// Sprawdzamy, czy wpis zawiera poszukiwany tekst .toLowerCase().indexOf(searchText.toLowerCase()) != -1) { 1 ) + "/" + date . getMonth + () alert("[" + blog[i].date.getDate() + "/" + (blog[i]. ); blog[i]. date .getFullYear() + "] " + blog[i]. body

if (blog[i].

body

break; } } lamy komunikat // Jeśli nie udało się odnaleźć poszukiwanego tekstu, wyświet if (i == blog.length ) tekstu."); alert("Przykro mi, ale żaden wpis nie zawierał poszukiwanego }

Jeśli wartość zmiennej i odpowiada długości tablicy, oznacza to, że pętla przeanalizowała wszystkie wpisy i nie udało się znaleźć żadnego, który zawierałby poszukiwany łańcuch znaków.

444

Rozdział 9.

ny wpis, Wyświetlając odnalezio datę w formacie y em tuj zen pre najpierw ną w nawiasach DD/MM/RRRR, zapisa samą treść wpisu. nie tęp nas klamrowych, a

Ożywianie danych

Teraz działa także wyszukiwanie! Oddana właśnie nowa wersja 2.0 strony MagicznaKostka udostępnia mechanizm przeszukiwania wpisów blogu, wykorzystujący wbudowane możliwości obiektu String. Stanowi ona doskonały przykład pokazujący, w jaki sposób obiekty mogą „ożywiać” dane — w naszym przypadku łańcuch znaków (czyli same dane) został zamieniony w byt zdolny do działania (przeszukania swojej zawartości). Być może najważniejsze jest jednak to, że wbudowane możliwości obiektu String uchroniły Renię od konieczności tworzenia własnej funkcji przeszukującej łańcuchy znaków i pozwoliły jej skoncentrować się na pisaniu swojego blogu.

Przeszukiwanie blogu — rewelacja!

Renia jest niesamowicie podekscytowana nową funkcją dodaną do swojego blogu, jednak daleko jej do tego, by spocząć na laurach. Już myśli o wersji 3.0 swojej strony…

jesteś tutaj  445

Naprawdę żadne z nich nie jest niemądre

Nie ma

niemądrych pytań

P: Wciąż nie do końca rozumiem, P: Czy w funkcji searchBlog()

P: Dlaczego w kodzie funkcji

O: Tak, to prawda. Każdy, nawet najmniejszy O: Nie. Metoda charAt() zwraca znak

O: Doskonałe pytanie! Odpowiedź na

jak to jest, że każdy łańcuch znaków jest obiektem. Czy to faktycznie prawda?

służącej do przeszukiwania wpisów w blogu można by użyć metody charAt() zamiast indexOf()?

łańcuch znaków w JavaScripcie jest obiektem. Jeśli w kodzie JavaScript zapiszesz swoje imię w cudzysłowach, na przykład ”Renia”, to utworzysz tym samym obiekt. Choć może się to wydawać lekką przesadą, to jednak takie podejście do łańcuchów znaków ma tę zaletę, że każdy łańcuch może wykonywać przydatne czynności, takie jak określenie swojej długości, wyszukanie fragmentu tekstu i tak dalej.

umieszczony we wskazanym miejscu łańcucha, zatem zastosowanie jej do odnalezienia ciągu znaków w łańcuchu, choć możliwe, byłoby bardzo czasochłonne i skomplikowane. Metoda indexOf() potrafi odszukać fragment łańcucha, a nie pojedynczy znak, dlatego najlepiej nadaje się do zastosowania w tej konkretnej sytuacji.

P: Rozumiem zatem, że łańcuch znaków też jest obiektem, lecz sprawia wrażenie, jakby jednocześnie był także tablicą… No wiesz, chodzi mi o te indeksy znaków. Czy zatem łańcuch znaków jest także tablicą?

O: Nie. Łańcuchy znaków na pewno nie są

tablicami. Jednak rzeczywiście wiele metod obiektu String operuje na danych łańcucha w taki sposób, jak gdyby były one tablicą znaków. Na przykład znaki w łańcuchu są indeksowane, zaczynając od 0, a dla każdego kolejnego znaku wartość indeksu rośnie o 1. Nie można jednak pobrać konkretnego znaku łańcucha, używając nawiasów kwadratowych ([]). A zatem — mimo że można wyobrazić sobie, iż znaki w łańcuchu przypominają elementy tablicy — nie można operować na zawartości obiektów String tak samo jak na zawartości obiektów Array.

P: Czy można wyszukać

w łańcuchu więcej niż jedno wystąpienie podanego ciągu znaków?

O: Owszem, można. Metoda indexOf()

domyślnie poszukuje tylko pierwszego wystąpienia podanego podłańcucha. Jednak w wywołaniu tej metody można przekazać drugi, opcjonalny argument, który określa, od jakiego miejsca łańcucha należy zacząć poszukiwania. Załóżmy zatem, że szukamy słowa ”kostka” i udało nam się je znaleźć w miejscu o indeksie 11. W takim przypadku możemy ponownie wywołać metodę indexOf(), przekazując w jej wywołaniu drugi argument o wartości 11, przez co wymusimy, że wyszukiwanie rozpocznie się od znaku o indeksie 12. A zatem ogólne rozwiązanie pozwalające na wyszukanie kilku wystąpień podłańcucha w pewnym łańcuchu polega na tym, by w wywołaniu metody indexOf() przekazać indeks podłańcucha odszukanego we wcześniejszym wyszukiwaniu.

searchBlog() zostały umieszczone dwa wywołania metody toLowerCase()?

nie jest związana z problemem wielkości liter podczas przeszukiwania łańcuchów znaków. Jeśli ktoś przeszukuje wpisy w blogu, próbując odnaleźć w nich słowo ”kostka”, to najprawdopodobniej chce, by zostały odszukane także słowa ”Kostka”, ”KOSTKA” itd. Często stosowane rozwiązanie tego problemu polega na skonwertowaniu obu łańcuchów znaków — poszukiwanego i przeszukiwanego — w taki sposób, by były zapisane literami o tej samej wielkości. Choć w funkcji searchBlog() została zastosowana metoda toLowerCase(), to równie dobrze można by zastosować metodę toUpperCase(). Chodzi o to, by wielkości liter w obu łańcuchach były identyczne.

Kluczowe zagadnienia 

Metoda toString() umożliwia przekształcenie dowolnego obiektu do postaci tekstowej.



Metody sort() obiektu Array można użyć do posortowania zawartości tablicy w dowolny wybrany sposób.



Tablice i łańcuchy znaków są w rzeczywistości zwyczajnymi obiektami, odpowiednio: Array oraz String. To właśnie te obiekty implementują metody, którymi możemy się posługiwać, operując na tablicach i łańcuchach znaków, oraz określają sposób przechowywania danych.



Metoda indexOf() obiektu String poszukuje wystąpień jednego łańcucha znaków wewnątrz drugiego i zwraca indeks określający położenie odnalezionego łańcucha.

446

Rozdział 9.

........................

........

........................ ....................... 14/08/.... 20.... 08............................ ........................ ........................ ................ Do....sta łam .... .... ........ wł....aś.... ni.... e no ....wą ....,.... za....mó....wi....on....ą.... Ożywianie danych uk.... ład.... an kę..... ................ .... ................ To....pr....aw .... ....dz ....iw e cu.... ........ de.... ńk.... o. ................ ............ ........................

........................ .......... ........................ .................... ........................ ........................ .. ..... ..... ..... ..... ........................ ..... .......... ..... ..... .... ..... .... ................ ..... .... ..... .... ........................ .................... ........................ ................................................................................................................ .............................. ........................ ................. ......... ..... ..... ..... ..... .... ..... .................... ..... ........................ .............................. ......................... ...................... ..... ................................................................................................................ .............................. ........ ..... ............... ......... ........................ ..... ..... .......... ..... ............był ..... ..... ..... ..... ........................ Renia nigdy nie przestaje myśleć o tym, co zrobić, by jej blog wciąż ciekawy ..... ..... ..... .... .......... ..... .... ..... ..... .... ..... ..... .... ..... ..... ..... .... ..... .... ........................ .......... ...................... ..... ................................................................................................................ ......... ..... ........ .............................. ..... ............ .... ..... ........................ ..... ...............się ..... ..... ..... ..... i inspirujący. Teraz wpadła ..... na..... nowy pomysł, który według niej może spodobać innym ..... ..... ..... ..... .... ..... ..... .... .... ..... ..... .... ..... .... ..... .... ..... .... ..... ............ ........................ .......... ......................... ............. ...................... ..... ................................................................................................................ ........ ..... ......... ....................układanek. .... .......... ........................ ..... miłośnikom Wymyśliła, by umieścić na swojej stronie ..... ...............Losowo, .......przycisk .......... ..... ........................ ..... ..... ......... .......... ........ .......... ............... .......... ..... .... ..... ..... ..... .... ..... .... .... .. ..... .... ..... ..... ........................ ............... ............... spowoduje wyświetlenie na stronie................................................................................................................ ..... ............... .... ................. ......... którego kliknięcie wpisów w..... losowej kolejności. ........................ .................... ..... ..... ..... ..... ..... .......... ..... ..... ........................ ..... ..... .... ..... ..... ..... .... ..... ..... .... ..... ..... .... ..... ..... .... ..... .... ........................ ............... ...................... ................................................................................................................ .... .... ..... .............................. ......... ..... ......... ........................ ..... ................................ .......... ..... ........................ ..... .......... .... .......... .... .......... .... .......... .................... ........................ .......... ..... ..... .... ..... .. ..... ..... ..... ..... ..... ..... ............ .......... ................................................................................................................ ......... ......................... ......... ..... ........ .......... ........................ ..... ................................ ..... ..... ........................ .... .......... .... .......... ............... ......... .......... ................................... ........................ .......... .......... .......... ..... .......... ....... ....... ..................... .... .... ........................... ................................................................................................................ .... ..................... ..... .... ..... .............................. ....... ................................ ............ ....... ..... ....... ..... .... ..... ........................ ..... ..... ..... ..... ..... ..... ..... ..... ..... .... ..... ..... .................... .... ..... ..... ..... .... ..... ................................... ..... .... ..... ............ ........................ ..... ..... .......... ....... ....... ................................... ............. ........................... ................................................................................................................ ..... ........ ......... .............................. ....... ....... ..... .... ....... ..... ........................ ..... ..... ..... ................................ ..... ..... ..... ..... ..... ..... .... .... ..... ..... .... .... ..... ..... ................ ........................ ................................... .......... ............ ............... ..... .......... ....... ....... ........................ ................................... ...................... ................................................................................................................ ..... .............................. .......... ....... .... ....... ..... ......... ....... ........................ ..... ..... ..... ................................ ..... ..... ..... .......... ..... ..... ........................ .... ..... ..... .... ..... ..... .... ..... ..... ................................... ..... .... ..... ..... .... ..... ..... .... ..... ............................ ........................ ............ ...................... ................................................................................................................ ..................... .... ..... .............................. .... ..... ....... ......... ....... ..... ......... ....... ..... ........................ ................................ ..... ..... .......... ........................ ......... ..... .... .......... .... .......... ............................ .................... ........................ .......... ..... .......... ..... ..... ....... ..... ....... ..... ....... ..... ....... .... ....... ..... ........ ............................ ........................... ................................................................................................................ ..... ............... .... ..... ....... ...................... ......... ......... ....... ..... ........ ....... ..... ..... ........................ ..... ..... ..... ..... ..... ..... ..... ..... ..... .... ..... ..... .................... ............................ ............... .................... ................................ ..... .......... ....... ..... ....... ....... ..................... ................ ........................... ................................................................................................................ ..................... ......... .............................. ......... ..... ....... ........ ....... ..... ....... ..... ..... ........................ ..... ..... ................................ ..... ..... ..... ..... ..... ..... .... ..... ..... .... .... ..... ..... ................ .... ..... ..... ................................. .... ..... ..... ..... .... ..... ..... ........................ ................................. ....... ..... ....... ........................... ............. ................................................................................................................ ..................... ..... ......................... ......... ....... ........ ....... ..... ....... ..... ........................ ..... ..... ..... ................................ ..... ..... ..... ..... ..... ..... .... .... ..... ..... .... .... ..... ..... .... .... ..... ..... ................................. ............ ........................ ..... ..... .......... ............ ....... ..... ....... ............ .......................... ..................... ................................................................................................................ ..................... ......................... .... ......................... ....... ....... ..... ....... .......... ..... ................................ ............................................ ..... ..... ..... .......... ..... .... ..... ......... .......... ..... .... ..... ..... ................................... .... .... ..... ..... ........ ..... .... ..... ..... .... .. ..... .... ..... .......................................... ........................ .......... ................................................................................................................ .............. .............................. .... .............................. ....... ....... ..... ....... ..... ..... ................................ ............................................ ..... ..... ..... .......... ..... .......... ..... ..... ..... ..... ..... ..... ................................... .... ..... ..... .... ..... ..... .... ..... .............. ..... ..... ....... ............ ....... ............................ ................................................................................................................ .............................. .............................. .............. ....... ................................ .......... .......... ..... .......... .......... .......... .......... ................................... .................... .......... ..... ..... ..... .. ..... ..... ..... ....... ..... ..... ....... .......................................... ................................................................................................................ ......................... .............................. .............. ....... ................................ .......... .......... .......... .......... .......... .......... ................................... .......... ......................... .......... ............ ..... ..... ....... ............ ....... .............. ..................................................................................................................................... Możliwość wyświetlania .............................. .............................. ....... ....... ....... ..... ..... ..... ..... ................................ ..... .......... ..... ..... ..... ..... ..... ..... ..... ..... ..... ..... ................................... ..... ..... ..... ..... w losowej kolejności ............ .. a — układankowa .......... ....... wpisów ....... ............... .............. .............. Reni .............................. ....................................................................................................................... ............................................................................ ..... mnicza ...............stronie .......... .......................................... ..... jest zabawna i dodaje blog ..... .. erka i taje ..... ..... .......................................... ..... ....................................................................................................................... ..................... ....................................................... eta. kobi ..... ..... nieco..... tajemniczości. .......................................... ......................... A ja lubię ................................... ...................... ..............i....... .............. .............................. zabawę tajemnice! .............................. .......................................... .............................. ................................... ................................... .......................................... .......................................... ............................ .......................................... .......................................... ............................ .......................................... .......................................... ............................ .......................................... .......................................... ............................ .......................................... .......................................... ............................ ..............................

..............................

..............................

Losowy wpisów 2008 14/08/wybór

19/08/2008 . nkę ada ukł oną ówi Udało mi się już ułożyć Dostałam właśnie nową, zam 14/08/2008 tę nową układankę. Już mnie o. zn eńk udziłwłaśnie cud e a, więc szu To prawdziw Dostałam nową, kamzamówioną czegoś noweukładankę. go. 14/08/2008 Toeprawdziwe ówioną układankę. ą, zamcudeńko. now m właśni16 tała Dos /0 8/ 20 8 08 200 19/08/ ie e cudeńko. mnziw ą układankęTo. Już prawd Już mnie głowa rozbolała Udało mi się już ułożyć tę now 19/08/2008 od zabaw tą nową kostk . ego now ą. goś cze Muszęmisobsięiejuż odpoułożyć znudziła, więc szukam cząć. tę nową układankę. Już mnie Udało 8 200 08/ 19/ mnie znudziła, więc czegoś nowego. nkę. Już ą układa nowszukam ułożyć tę Udało mi się już 21/0 14/0 8/20 8/2008 8 20008 16/08/ czegoś nowego. ła,. więcZnszu aw tą nową kos dzitką od zab bolaą,łazam Dostmn alakam roznow ałam złam w internecie układ wanie właś ie gło Już ówioną ukłaznu 16/08/2008 dankę. ankę 7×7×7. O rany! Zaba To ocząć. wa będ dziw ie odp e cude ńko. zie su szę sob Mupraw per. od zabaw tą nową kostką. Już mnie głowa rozbolała 16/08/2008 Muszę sobieła odpocząć. od zabaw tą nową kostką. bola roz ie głowa mn Już 29 19/0 /0 8/20 8/ 08 20 8 08 200 21/08/ y! ie odpocząć. ransob adankę 7×7×7.MuOszę ecietęukł Uda ernyć kałam się z kilkoma in jużintułoż mw złasię alami now Znło ą układankę. Już mnie Spot 21/08/2008 nymi zapaleńcami, by er. czegoś nowego. znud porozmawiwaćinternecie supam ziła,a będ więczieszuk Zabaw o układanukładankę Znalazłam 7×7×7. iu kostki 7× 7×7. O rany! 21/08/2008I szczerze mówiąc y! ran , O m Zabawa będzie super. 7. am 7× 7× mi nkę es zane uczucia. Znalazłam w internecie układa 16/0 08 8 08/200 29/8/20 er. , mibawa będzie sup ymi zapaleńcaZa komaodinn JużSpot mnikał z kilolała e głow a rozb am się zaba w tą nową7.kostką. 29/08/2008 7× 7× tki Musbyzępor o ukł sobie awiać cząć ozmodpo . adaniu kos a. 08/2008Spotkałam się z kilkoma innymi zapaleńcami, ane uczuci29/ c, I szczerze mówią mam miesz mi, 7×7×7. by porozmawiać o układaniu kostki i zapaleńca Spotkałam się z kilkoma innym 21/08/2008 I szczerze mówiąc, mam mieszane 7×7. uczucia. tki 7× adaniu kos Znalazłam w internecie układankę 7×7×7. O by porozmawiać o ukł ucz rany! ane ucia. I szczerze mówiąc, mam miesz Zabawa będzie super.

29/08/2008 Spotkałam się z kilkoma innymi zapaleńcami, by porozmawiać o układaniu kostki 7×7×7. I szczerze mówiąc, mam mieszane uczucia.

WYTĘŻ umysł

Jak można by losowo wybierać wpisy z blogu Reni?

jesteś tutaj  447

Wszystko ma swoje miejsce

Obiekt Math jest obiektem organizacyjnym Aby pomóc Reni w wyborze losowych wpisów z jej blogu, potrzebny nam jest generator liczb losowych. Generator taki dostępny jest w języku JavaScript, lecz aby z niego skorzystać, musimy użyć pewnego wbudowanego obiektu, który nie jest aż tak „ożywiony” jak inne obiekty, jakich używaliśmy do tej pory. Obiektem tym jest Math. Obiekt ten jest unikalny w języku JavaScript, gdyż nie zawiera żadnych danych, które mogą ulec zmianie, ani żadnych metod, które operują na jego wewnętrznych danych. Zaokrągla liczbę zmiennopozycyjną do całkowitej.

Math

Zaokrągla liczbę zmiennopozycyjną w DÓŁ do liczby całkowitej.

round()

floor()

PI

ceiling() random()

Stała matematyczna — 3,14

Zaokrągla liczbę zmiennopozycyjną w GÓRĘ do liczby całkowitej.

Generuje liczbę losową z zakresu od 0 do 1.

Obiekt Math jest obiektem organizacyjnym. Oznacza to, że jest on zbiorem metod wykonujących pewne operacje matematyczne oraz stałych matematycznych. Nie ma w nim żadnych zmiennych, co oznacza, że obiekt Math nie musi zachowywać stanu. Jedynymi danymi, jakie ten obiekt zawiera, jest kilka stałych, takich jak PI (3,14). Pomimo to metody obiektu Math są bardzo przydatne. Na przykład metoda random() generuje losową liczbę zmiennopozycyjną z zakresu od 0 do 1.

Obiekt Math jest obiektem organizacyjnym, grupującym metody oraz stałe matematyczne.

Podaj wyniki przedstawionych poniżej wywołań metod obiektu Math.

ćwiczenie

Math.round(Math.PI)

.........................................

Math.ceiling(Math.PI)

.........................................

Math.random()

......................................... Odpowiedzi znajdziesz na stronie 450

448

Rozdział 9.

Ożywianie danych

Obiekt Math bez tajemnic Temat dzisiejszego wywiadu:

Kłótnia w zespole funkcji matematycznych Head First: Hm… Muszę przyznać, że wszystko mi się

pomieszało. Jesteś obiektem, lecz dochodzą mnie słuchy, że zajmujesz się jedynie przechowywaniem kilku metod i stałych matematycznych. Przypuszczałem, że cały sens obiektów polega na „ożywieniu” danych. No wiesz… zapisaniu jakichś danych w obiekcie i robieniu z nimi różnych odlotowych rzeczy.

Math: Tak sądzą ludzie, którzy wierzą pogłoskom

krążącym wśród programistów używających JavaScriptu; jednak prawda jest taka, że nie dla wszystkich obiektów celem nadrzędnym jest ożywianie danych. Całkowicie prawidłowym i dobrym rozwiązaniem jest przydzielenie obiektowi roli organizatora — właśnie takiej, jaką ja pełnię.

Head First: Ale czy w takim razie nie można by

zaimplementować tych wszystkich metod jako zwyczajnych funkcji?

Math: Oczywiście, że by można… Zapominasz jednak, że język JavaScript bazuje na obiektach. Zatem w rzeczywistości nie ma czegoś takiego jak „niezależna funkcja”.

Head First: Ale mogę przecież utworzyć funkcję poza

obiektem i będzie ona działać bardzo dobrze.

Math: No jasne, ale w rzeczywistości wszystkie funkcje

są metodami, gdyż zawsze należą do jakiegoś obiektu, nawet jeśli jest on ukryty. To pomaga zrozumieć, dlaczego w JavaScripcie nie ma czegoś takiego jak „standardowa funkcja”.

Head First: Rozumiem. W takiej sytuacji umieszczenie

w tobie tych wszystkich metod matematycznych zaczyna wyglądać całkiem sensownie.

Math: Nie zapominaj także, że sam fakt braku danych wewnętrznych, na jakich mogę operować, nie oznacza wcale, że nie odgrywam znaczącej roli jako obiekt.

Head First: Co masz na myśli? Math: Cóż… Wyobraź sobie grupę ludzi mających te

same zainteresowania, na przykład układanki. W wielu przypadkach takie osoby organizują się, by wszyscy mogli dzielić się swoimi zainteresowaniami. Choć metody matematyczne nie lubią przebywać w grupach w równie dużym stopniu co ludzie, to jednak sposób organizacji, jaki im zapewniam, na pewno przynosi im korzyści.

Head First: Chodzi ci o to, że one wszystkie mają jakieś wspólne zainteresowania?

Math: Właśnie! A tym zainteresowaniem jest wykonywanie operacji matematycznych, takich jak zaokrąglanie liczb, wykonywanie obliczeń trygonometrycznych czy też generacja liczb losowych.

Head First: Wspomniałeś o generacji liczb losowych.

Słyszałem, że te liczby nie są tak naprawdę losowe. Czy w tej plotce jest choć trochę prawdy?

Math: Muszę przyznać, że plotka jest prawdziwa,

generowane przez moje metody liczby nie są tak naprawdę losowe. Podobnie zresztą jak większość innych generowanych komputerowo liczb losowych. Mój generator jest tak naprawdę „pseudolosowy” i całkowicie wystarcza do większości zastosowań.

Head First: Pseudolosowy, czyli tak jakby

pseudonaukowy… albo mający coś wspólnego z pseudokodem?

Math: Hm… i tak, i nie. Nie — bo nie ma on nic

wspólnego z żadną pseudonauką. Tak — troszeczkę jak pseudokod, gdyż pseudokod służy do reprezentacji pomysłu na działanie pewnego fragmentu kodu, a jednocześnie sam nie jest kodem. Liczby pseudolosowe „starają się” dążyć do losowości, choć w rzeczywistości nigdy całkowicie losowe nie będą.

Head First: Czy zatem mogę uznać, że liczby pseudolosowe są wystarczająco losowe, by zaspokoić potrzeby większości aplikacji pisanych w JavaScripcie?

Math: Owszem, swoją drogą jest na to dobre określenie: „wystarczająco losowe”. Zapewne nie będziesz chciał używać moich liczb pseudolosowych w sprawach związanych z bezpieczeństwem narodowym, jednak w zupełności wystarczą do zapewniania źródła losowości w normalnych skryptach. Head First: Rozumiem. Dobrze… dziękuję bardzo za

poświęcony czas… i za szczerość odnośnie liczb losowych.

Math: Ja również się cieszę… w końcu wiesz… ja nie mogę kłamać.

jesteś tutaj  449

Rozwiązanie ćwiczenia

Podaj wyniki przedstawionych poniżej wywołań metod obiektu Math.

ćwiczenie Rozwiązanie

3,14 po zaokrągleniu daje 3.

Math.round(Math.PI) Math.ceiling(Math.PI) Math.random()

3 ................................ 4 ................................ ? ................................

Wartość stałej PI wynosi 3,14.

3,14 po zaokrągleniu w górę daje 4.

Przykro mi, ale to było podchwytliwe pytanie! Nie możesz odpowiedzieć na nie, gdyż wynik tej metody jest losowy.

Generowanie liczb losowych przy użyciu metody Math.random() Pseudolosowe czy nie, niemniej liczby losowe generowane przez metodę random() są niezwykle przydatne w aplikacjach takich jak MagicznaKostka, które muszą losowo wybrać jedną spośród kilku informacji. Problem polega jednak na tym, że metoda ta zwraca liczbę z zakresu od 0 do 1, natomiast Renia potrzebuje liczby losowej z zakresu od 0 do górnego zakresu tablicy. Innymi słowy, Renia musi wygenerować losowe liczby całkowite, które będzie można zastosować jako indeksy tablicy.

alert(Math.random()); alert(Math.random()); alert(Math.random());

Aby wygenerować liczbę losową z zakresu innego niż od 0 do 1, będziemy musieli skorzystać z kilku działań arytmetycznych i jeszcze jednej funkcji obiektu Math. Metoda floor() zaokrągla liczbę w dół do najbliższej liczby całkowitej i doskonale nadaje się do generowania losowych liczb całkowitych należących do podanego zakresu.

0 - 5

var rzutKostka = Math.floor(Math.random() * 6) 1 - 6

450

Rozdział 9.

+ 1;

Każda z liczb losowych jest z zakresu od 0 do 1.

Ożywianie danych

Nie ma

niemądrych pytań

P: Dlaczego przed użyciem

P: Jaka jest różnica pomiędzy

O: Ech… to wnikliwe pytanie i dotyka

O: Metoda round() zaokrągla liczbę

obiektu Math nie trzeba go utworzyć?

niezwykle ważnego zagadnienia związanego z obiektami. Ponieważ obiekt Math nie zawiera żadnych danych, które mogłyby ulegać zmianom (nazywanych także danymi składowymi), zatem nie ma potrzeby, by tworzyć ten obiekt. Pamiętaj, że obiekt Math jest jedynie kolekcją metod statycznych oraz stałych, a zatem wszystko, co ma być w tym obiekcie, już w nim jest — nie musimy niczego tworzyć. Zagadnienia te nabiorą większego sensu w rozdziale 10., kiedy dowiesz się znacznie więcej o instancjach obiektów oraz o klasach.

metodami round() oraz floor() obiektu Math?

w górę lub w dół, zależnie od wartości jej części dziesiętnej. Na przykład wywołanie Math.round(11.375) zwróci liczbę 11, natomiast wywołanie Math. round(11.754) zwróci liczbę 12. Natomiast metoda floor() zawsze zaokrągla w dół, niezależnie od wartości części dziesiętnej zaokrąglanej liczby. Możesz sobie zatem wyobrazić, że działanie tej metody polega na odcięciu części dziesiętnej liczby.

P: Co jeszcze potrafi obiekt Math? O: Bardzo wiele. Dwoma bardzo przydatnymi funkcjami, z których nie musieliśmy jeszcze korzystać, są min() oraz max(). Analizują one dwie przekazane w wywołaniu liczby i zwracają odpowiednio mniejszą lub większą z nich. Kolejną bardzo przydatną metodą jest abs() — zwraca ona wartość dodatnią, niezależnie od tego, czy liczba przekazana w wywołaniu była większa, czy mniejsza od zera.

Dla maniaków Jeśli pracujesz nad aplikacją, w której konieczne jest zastosowanie prawdziwie losowych liczb, to zajrzyj na witrynę http://random.org; znajdziesz tam informacje o tym, jak wyjść poza świat liczb pseudolosowych.

Zaostrz ołówek Napisz kod funkcji randomBlog(), która ma losowo wybierać wpisy z blogu Reni i wyświetlać je w informacyjnym okienku dialogowym. Podpowiedź: wpis wyświetlany w okienku dialogowym może być sformatowany w taki sam sposób jak wynik funkcji searchBlog(). .................................................................................................................................................................... .................................................................................................................................................................... .................................................................................................................................................................... .................................................................................................................................................................... .................................................................................................................................................................... ....................................................................................................................................................................

jesteś tutaj  451

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie Do wybrania konkretnego wpisu w blogu używamy liczby losowej.

Napisz kod funkcji randomBlog(), która ma losowo wybierać wpisy z blogu Reni i wyświetlać je w informacyjnym okienku dialogowym. Podpowiedź: wpis wyświetlany w okienku dialogowym może być sformatowany w taki sam sposób jak wynik funkcji searchBlog().

Generujemy liczbę losową z zakr esu od zera od liczby wpisów w blog u pomniejszonej o jeden.

function randomBlog() { .................................................................................................................................................................... // Wybieramy liczbę losową z zakresu od 0 do blog.length - 1 .................................................................................................................................................................... var i = Math.floor(Math.random() * blog.length - 1); .................................................................................................................................................................... alert("[" + blog[i].date.Date()+ "/" + (blog[i].date.getMonth() + 1) + "/" + .................................................................................................................................................................... blog[i].date.getFullYear() + "] " + blog[i].body) ; .................................................................................................................................................................... } .................................................................................................................................................................... Formatujemy wpis, wyświetlając najpierw datę w formacie DD/MM/RRRR, a następnie treść wpisu.

Losowo, lecz wciąż nie do końca dobrze Teraz blog Reni udostępnia już możliwość wyświetlania losowego wpisu, co bardzo cieszy jego właścicielkę. Użytkownicy mogą teraz przeglądać blog na stronie MagicznaKostka i odczuwać niepewność i zaciekawienie, gdyż nie będą wiedzieli, jaki wpis zostanie wyświetlony.

Losowo wybrany wpis w blogu Reni.

Pomimo podniecenia towarzyszącemu zaimplementowaniu nowej możliwości Renia wciąż ma dziwne uczucie, że na stronie MagicznaKostka czegoś brakuje. Jej „obiekt” Blog składa się aktualnie z dwóch właściwości, a wszelkie operacje na tych obiektach są wykonywane przez funkcje zewnętrzne. Nie wygląda to na wzorcowy projekt obiektowy…

452

Rozdział 9.

Ożywianie danych

Obiekt w pogoni za akcjami Odczucia Reni dotyczące obiektu Blog używanego na stronie MagicznaKostka są trafne i słuszne. Behawioralna część tego obiektu jest niezwykle uboga i wymagałaby gruntownej przeróbki i zaimplementowania metod do obsługi przeróżnych zadań związanych z blogiem. Renia potrzebuje metod, które dodałyby nieco akcji do obiektu Blog!

Naprawdę mogłabym używać metod blogu.

Zaostrz ołówek Przeanalizuj kod strony MagicznaKostka i zaznacz w kółkach ten, który według Ciebie nadaje się do umieszczenia w metodach obiektu Blog. Pamiętaj przy tym, by każdej z metod nadać nazwę. function showBlog(numEntries) { // W pierwszej kolejności sortujemy blog w odwrotnej kolejności chronologicznej // (najnowszy na początku) blog.sort(function(blog1, blog2) { return blog2.date - blog1.date; }); // Aktualizujemy liczbę wpisów, aby w razie konieczności wyświetlić wszystkie wpisy if (!numEntries) numEntries = blog.length; // Wyświetlamy poszczególne wpisy var i = 0, blogText = ""; while (i < blog.length && i < numEntries) { // Co drugi wpis będzie wyświetlany na szarym tle if (i % 2 == 0) blogText += "

"; else blogText += "

"; // Generujemy sformatowany kod HTML blogText += "" + blog[i].date.getDate() + "/" + (blog[i].date.getMonth() + 1) + "/" + blog[i].date.getFullYear() + "
" + blog[i].body + "

"; }

}

i++;

// Wyświetlamy kod HTML blogu na stronie document.getElementById("blog").innerHTML = blogText;

// Przegląda listę wpisów w poszukiwaniu podanego fragmentu tekstu function searchBlog() { var searchText = document.getElementById("searchtext").value; for (var i = 0; i < blog.length; i++) { // Sprawdzamy, czy wpis zawiera poszukiwany tekst if (blog[i].body.toLowerCase().indexOf(searchText.toLowerCase()) != -1) { alert("[" + blog[i].date.getDate() + "/" + (blog[i].date.getMonth() + 1) + "/" + blog[i].date.getFullYear() + "] " + blog[i].body); break; } }

}

// Jeśli nie udało się odnaleźć poszukiwanego tekstu, wyświetlamy komunikat if (i == blog.length) alert("Przykro mi, ale żaden wpis nie zawierał poszukiwanego tekstu.");

function randomBlog() { // Wybieramy liczbę losową z zakresu od 0 do blog.length – 1 var i = Math.floor(Math.random() * blog.length – 1); alert("[" + blog[i].date.getDate() + "/" + (blog[i].date.getMonth() + 1) + "/" + blog[i].date.getFullYear() + "] " + blog[i].body); }

jesteś tutaj  453

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie

Przeanalizuj kod strony MagicznaKostka i zaznacz w kółkach ten, który według Ciebie nadaje się do umieszczenia w metodach obiektu Blog. Pamiętaj przy tym, by każdej z metod nadać nazwę.

function showBlog(numEntries) { // W pierwszej kolejności sortujemy blog w odwrotnej kolejności chronologicznej // (najnowszy na początku) blog.sort(function(blog1, blog2) { return blog2.date - blog1.date; }); // Aktualizujemy liczbę wpisów, aby w razie konieczności wyświetlić wszystkie wpisy if (!numEntries) numEntries = blog.length; // Wyświetlamy poszczególne wpisy var i = 0, blogText = ""; while (i < blog.length && i < numEntries) { // Co drugi wpis będzie wyświetlany na szarym tle if (i % 2 == 0) blogText += "

"; else blogText += "

";

Konwersja wpisu blogu na sformatowany kod HTML — Blog.toHTML().

// Generujemy sformatowany kod HTML blogText += "" + blog[i].date.getDate() + "/" + (blog[i].date.getMonth() + 1) + "/" + blog[i].date.getFullYear() + "
" + blog[i].body + "

"; }

}

i++;

// Wyświetlamy kod HTML blogu na stronie document.getElementById("blog").innerHTML = blogText;

// Przegląda listę wpisów w poszukiwaniu podanego fragmentu tekstu function searchBlog() { var searchText = document.getElementById("searchtext").value; for (var i = 0; i < blog.length; i++) { // Sprawdzamy, czy wpis zawiera poszukiwany tekst if (blog[i].body.toLowerCase().indexOf(searchText.toLowerCase()) != -1) { alert("["+ blog[i].date.getDate() + "/" + (blog[i].date.getMonth() + 1) + "/" + blog[i].date.getFullYear() + "] " + blog[i].body); break; } }

}

// Jeśli nie udało się odnaleźć poszukiwanego tekstu, wyświetlamy komunikat if (i == blog.length) alert("Przykro mi, ale żaden wpis nie zawierał poszukiwanego tekstu.");

ext() Blog.containsT

Blog.toString()

function randomBlog() { // Wybieramy liczbę losową z zakresu od 0 do blog.length – 1 var i = Math.floor(Math.random() * blog.length – 1); alert("[" + blog[i].date.getDate() + "/" + (blog[i].date.getMonth() + 1) + "/" + blog[i].date.getFullYear() + "] " + blog[i].body); }

Blog.toHTML() Konwertuje wpis w blogu na odpowiednio sformatowany kod HTM co jest ogromnym ułatwieniem dla L, innych fragmentów kodu, które takż będą chciały wyświetlić odpowied e nio sformatowany wpis.

454

Rozdział 9.

Blog.containsText() To niewielki fragment kodu, lecz aci warto go zaimplementować w post metody, gdyż wpis może kiedyś zostać wyposażony w możliwość ci. przeszukiwania swojej zawartoś

Blog.toString() Konwertuje wpis w blogu na zwyczajny łańcuch znaków; z powodzeniem będziemy mogli używać tej funkcji, gdy będziemy chcieli wyświetlić treść wpisu poprzedzoną datą zapisaną w nawiasach klamrowych.

Ożywianie danych

Nie ma

niemądrych pytań

P: Skąd wiadomo, jaki fragment

kodu należy umieścić w metodzie?

O: Cóż, przede wszystkim musisz

zastanowić się nad tym, do czego ma służyć metoda, oraz założyć, że powinna wykonywać jakieś czynności na podstawie aktualnego stanu (zawartości) obiektu. W pewnym stopniu określanie metod obiektu odpowiada określaniu czynności, jakie obiekt wykonuje bądź jakie powinien wykonywać. Następnie należy się skoncentrować na zapewnieniu obiektom możliwości wykonywania operacji na samych sobie.

Na przykład całkiem sensowne jest zaimplementowanie w obiekcie Blog metod, które będą zapisywać zawartość obiektu w formie zwyczajnego tekstu lub sformatowanego kodu HTML, gdyż obie te operacje wymagają dostępu do wewnętrznych danych blogu. Podobnie akcją, która powinna być wbudowana w obiekt Blog, jest poszukiwanie wpisów zawierających podany fragment tekstu; dlatego także i ona doskonale nadaje się do zaimplementowania w postaci metody.

P: Czy jest jakiś przykład akcji,

O: Przykładem jednej z akcji, których obiekt Blog nie powinien wykonywać, może być prezentowanie i wyszukiwanie listy wpisów spełniających jakieś zadane warunki. Wynika to z faktu, że obiekt Blog reprezentuje jeden wpis w blogu. To właśnie dlatego tablica blog zawiera grupę obiektów Blog. Sam obiekt Blog nie musi „zaprzątać sobie głowy” kolekcją innych obiektów tego typu. Powinien natomiast zajmować się swoimi własnymi sprawami, czyli wykonywaniem operacji na podstawie własnej zawartości.

której obiekt Blog nie powinien wykonywać?

Zamiana funkcji na metodę A zatem — skoro wskazaliśmy już fragmenty kodu używanego na stronie MagicznaKostka, które nadawałyby się do zaimplementowania w postaci metod obiektu Blog — możemy przyjrzeć się procesowi konwersji jednego z nich na metodę. Konkretnie rzecz biorąc, zajmiemy się metodą containsText(), której zadaniem będzie przeszukanie treści wpisu i sprawdzenie, czy zawiera on podany łańcuch znaków. Przeniesienie kodu przeszukującego do metody wiąże się przede wszystkim z bezpośrednim operowaniem na właściwości body obiektu Blog, a nie na zmiennej lokalnej, jakiej używaliśmy do tej pory w funkcji searchBlog(). Proces przekształcania funkcji na metodę pomogą Ci zrozumieć poniższe punkty: 1

Deklarujemy metodę i uzupełniamy listę argumentów, jeśli jest potrzebna. W naszym przypadku znajdzie się na niej argument określający wyszukiwany tekst.

2

Przenosimy istniejący kod do nowej metody.

3

Modyfikujemy kod metody tak, by odwoływał się do właściwości obiektu, na przykład: this.body.

Blog date

toString()

"14/08/2008" toHTML() Dostałam właśnie nową, zamówioną układankę...

body

containsText()

Zaostrz ołówek Napisz kod metody containsText() obiektu Blog; metoda ta ma być tworzona w konstruktorze Blog() poprzez przypisanie literału funkcyjnego do właściwości this.containsText. ..................................................................................................................................................................................................... ..................................................................................................................................................................................................... .....................................................................................................................................................................................................

jesteś tutaj  455

Szybka zmiana

Zaostrz ołówek Rozwiązanie Metodę tworzymy, przypisując literał funkcyjny do właściwości.

Napisz kod metody containsText() obiektu Blog; metoda ta ma być tworzona w konstruktorze Blog() poprzez przypisanie literału funkcyjnego do właściwości this.containsText.

this.containsText = function(text) { ..................................................................................................................................................................................................... return (this.body.toLowerCase().indexOf(text.toLowerCase()) != -1); ..................................................................................................................................................................................................... }; ..................................................................................................................................................................................................... Słowo kluczowe this służy nie tylko do tworzenia właściwości, lecz także metod.

Kod wewnątrz metody operuje bezpośrednio na właściwościach obiektu, używając przy tym słowa kluczowego this.

Przedstawiamy piękny nowy obiekt Blog Pozostałe dwie metody dołączyły do containsText(), tworząc zupełnie nową wersję obiektu Blog, która posiada już nie tylko właściwości, lecz także zachowania. function Blog(body, date) { // Przypisanie właściwości this.body = body; this.date = date;

}

456

Tworzymy i inicjalizujemy ości obiektu. właściw

Hm… całkiem nieźle mnie dopracowali!

Metoda toString() zwraca wpis w blogu zapisany w postaci zwyczajnego tekstu.

// Generowanie tekstowej reprezentacji wpisu this.toString = function() { + 1) + "/" + return "[" + this.date.getDate() + "/" + (this.date.getMonth() dy; this.bo + " "] + () this.date.getFullYear Metoda toHTML() zwraca wpis w }; blogu zapisany w postaci atrakcyjnie sformatowanego kodu HTML. // Generowanie wpisu w postaci sformatowanego kodu HTML { ight) n(highl this.toHTML = functio o w argumencie // Używamy szarego tła jako podświetlenia, jeśli tak zażądan var blogHTML = ""; : "

"; blogHTML += highlight ? "

" blogu w wpisu HTML kod owany // Generujemy sformat ate.getMonth() + 1) + "/" + blogHTML += "" + this.date.getDate() + "/" + (this.d "

"; + dy this.bo + />" ng>

Rozdział 9.

Ożywianie danych

Jakie są korzyści użycia obiektów na stronie MagicznaKostka? Dopiero po umieszczeniu nowej wersji obiektu Blog w kodzie strony MagicznaKostka (znajdziesz ją wraz z kodami wszystkich przykładów prezentowanych w książce na serwerze FTP wydawnictwa Helion: ftp://ftp.helion.pl/przyklady/hfjsc.zip) możemy w pełni docenić zalety, jakie zapewnia programowanie obiektowe. Teraz, kiedy kilka istotnych zadań zostało przekazanych do obiektu Blog, skrypty używane na stronie Reni zostały zdecydowanie uproszczone.

Zastosowanie nowego obiektu Blog pozwoliło uprościć kod JavaScript na stronie MagicznaKostka.

// Prezentacja listy wpisów function showBlog(numEntries) { // W pierwszej kolejności sortujemy wpisy w odwrotnej kolejności chronologicznej. blog.sort(function(blog1, blog2) { return blog2.date - blog1.date; }); // W razie konieczności modyfikujemy liczbę wpisów, by wyświetlić pełny blog if (!numEntries) numEntries = blog.length; // Wyświetlamy poszczególne wpisy var i = 0, blogListHTML = ""; while (i < blog.length && i < numEntries) { blogListHTML += blog[i].toHTML(i % 2 == 0); i++; }

Metoda toHTML() jest w całości odpowiedzialna za sformatowanie wpisu w postaci kodu HTML.

// Dodajemy kod HTML z wpisami do strony document.getElementById("blog").innerHTML = blogListHTML; } // Przegląda listę wpisów w poszukiwaniu podanego fragmentu tekstu function searchBlog() { var searchText = document.getElementById("searchtext").value; Metoda containsText() zajmuje for (var i = 0; i < blog.length; i++) { się odnalezieniem w treści wpisu podanego łańcucha znaków. // Sprawdzamy, czy wpis zawiera poszukiwany tekst if (blog[i].containsText(searchText)) { alert(blog[i]); Metoda toString() jest nieco bardziej break; wyrafinowana; jest ona wywoływana automatycznie za każdym razem, gdy } obiekt zostanie użyty w kontekście, } znaków. w jakim oczekiwano łańcucha

// Jeśli nie udało się odnaleźć poszukiwanego tekstu, wyświetlany komunikat if (i == blog.length) alert("Przykro mi, ale żaden wpis nie zawierał poszukiwanego tekstu."); } // Wyświetlamy losowo wybrany wpis z blogu function randomBlog() { // Wybieramy liczbę losową z zakresu od 0 do blog.length – 1 var i = Math.floor(Math.random() * blog.length); alert(blog[i]); }

jesteś tutaj  457

Gotowy na zaszczyty

MagicznaKostka wersja 3.0 Zrobił się z tego całkiem spory projekt, ale Renia w końcu uznała, że trzecia wersja strony MagicznaKostka jest na tyle dobra, że może sobie zrobić przerwę w pracy z JavaScriptem i zająć się tym, co lubi najbardziej — układankami. Poza tym Renia jest bardzo podekscytowana przygotowaniami na imprezkę, na którą została zaproszona… Wpisy w blogu są posortowane i przejrzyście sformatowane…

…poza tym blog można przeszukiwać…

…wpisy można także przeglądać losowo…

…a wszystko to dzięki niestandardowemu obiektowi o nazwie Blog!

Blog body

toString()

Dostałam właśnie toHTML() nową, zamówioną układankę... 14 sierpnia 2008

date

458

Rozdział 9.

containsText()

Kto by się spodziewał, że obiekt Blog stanie się moją ulubioną układanką?

Ożywianie danych

Zaginarka stron Zegnij stronę wzdłuż pionowych linii, tak by oba mózgi się połączyły, a następnie rozwiąż zagadkę.

Co obiekty JavaScript mogą zrobić dla swoich danych? To spotkanie umysłów!

na… Zapraszamy Cię

wą! Imprezę puzzlo Data: Miejsce:

24 października 34 ul. Wy tężonego umysłu 12/ Geniuszowice 12-345 Polska

Stri

ng

Date Math Renia, miłośniczka układanek 23 Aleja Wycinanek 78-098 Łamigłowice Polska

Ar r

ay

Szukaj jeśli chcesz, lecz każde poszukiwanie da ten sam wynik — wykaże, że to obiekty JavaScriptu najlepiej nadają się do takich operacji jak aktualizowanie i sortowanie danych. Pozwalają one nawet na generowanie liczb losowych

jesteś tutaj  459

460

Rozdział 9.

10. Tworzenie własnych obiektów

Zrób to po swojemu, używając własnych obiektów Zdecyduj się już teraz, pomiń wszystkich pośredników, masz gwarancję zwrotu kosztów za jednego dolara… jeśli zamówisz już teraz. Możesz to zrobić po swojemu, kotku!

Gdyby to było takie łatwe, na pewno byś sobie z tym sam poradził. W świecie JavaScriptu nie ma gwarancji zwrotu poniesionych kosztów, jednak nikt nigdy nie będzie Ci bronił robić wszystkiego po swojemu. Niestandardowe obiekty w JavaScripcie są odpowiednikiem świetnej, wielkiej, gorącej, mistrzowskiej, okazyjnej promocji. To jeden niestandardowy kubek kawy! A dzięki własnym obiektom JavaScriptu możesz zaparzyć sobie kawę, która zrobi dokładnie to, co będziesz chciał — a wszystko to dzięki korzyściom, jakie zapewniają właściwości i metody. W efekcie powstaje obiektowy, nadający się do wielokrotnego stosowania kod, który rozszerza możliwości języka JavaScript… tylko i wyłącznie dla Ciebie!

to jest nowy rozdział  461

Metody Blog: och! ach!

Ponowna analiza metod obiektu Blog Kiedy rozstaliśmy się z Renią w poprzednim rozdziale, była niezwykle podekscytowana stworzeniem własnego, obiektowego blogu, który umożliwił jej dzielenie się z innymi swoją pasją do układanek. Choć tworząc obiekt Blog, Renia wykonała całkiem dobrą robotę, to jednak zupełnie nieświadomie przegapiła doskonałą okazję do zastosowania technik obiektowych w pozostałej części skryptów na stronie MagicznaKostka. Co ważniejsze, Renia nie zbadała dokładnie wszystkich możliwości poprawienia wydajności obiektu Blog i poprawienia jego organizacji; innymi słowy, przegapiła okazję, by uprościć sobie późniejsze utrzymanie i rozbudowę obiektu. Ostatnią modyfikacją wprowadzoną przez Renię w obiekcie Blog było dodanie do niego trzech metod, obsługujących kilka operacji związanych z korzystaniem z blogu.

Blog body

date

Najnowszą wersję kodów znajdzie sz na serwerze FTP wydawnictwa Heli ftp://ftp.helion.pl/przyklady/hfjsc. on: zip.

toString()

Spotkałam się z kilkoma innymi zapaleńcami, by porozmawiać o układaniu

Blog Reni na stronie MagicznaKostka działa, jednak nie jest zachwycającym przykładem projektowania i programowania obiektowego. Trzy metody obiektu Blog realizują kilka zadań, które dobrze jest wykonywać z poziomu wpisu do blogu.

toHTML()

29 sierpnia 2008 containsText()

Bardzo mi się podobają metody obiektu Blog.

date) { function Blog(body, wości // Przypisanie właści this.body = body; this.date = date;

su wej reprezentacji wpi // Generowanie teksto "/" + { ) on( cti fun = g e.getMonth() + 1) + this.toStrin te()+ "/" + (this.dat tDa .ge ate s.d thi + return "[" ; () + "] " + this.body this.date.getFullYear }; anego kodu HTML w postaci sformatow // Generowanie wpisu { ) umencie on(highlight li tak zażądano w arg this.toHTML = functi o podświetlenia, jeś jak tła o reg sza y // Używam : "

"; var blogHTML = ""; nd-color:#EEEEEE'>" ? "

" : "

";

};

// Generujemy sformatowany kod HTML wpisu w blogu blogHTML += "" + this.date.getDate() + "/" + (this.date.get Month() + 1) + "/" + this.date.getFullYear() + "
" + this.body + "

"; return blogHTML;

// Sprawdza, czy treść blogu zawiera podany łańcuch znaków Blog.prototype.containsText = function(text) { return (this.body.toLowerCase().indexOf(text.toLowerCase()) != -1); };

jesteś tutaj  469

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie

Kod obiektu Blog korzysta już z obiektu prototype, dzięki czemu jego metody należą do klasy. Oznacz odpowiedzialny za to kod i wyjaśnij, jak działa.

function Blog(body, date) { // Przypisanie wartości this.body = body; this.date = date; }

Teraz konstruktor zajmuje się jedynie utworzeniem i zainicjowaniem właściwości.

// Generowanie tekstowej reprezentacji wpisu Blog.prototype.toString = function() { return "[" + this.date.getDate() + "/" + (this.date.getMonth()+ 1) + "/" + this.date.getFullYear() + "] " + this.body; Ponieważ metody nie są przypisywane }; ej instancji obiektu do żadnej konkretn

Blog, zatem przypisanie następuje // Generowanie wpisu w postaci sformatowanego kodu HTML poza konstruktorem. Blog.prototype.toHTML = function(highlight) { // Używamy szarego tła jako podświetlenia, jeśli tak zażądano w argumencie var blogHTML = ""; blogHTML += highlight ? "

" : "

";

};

// Generujemy sformatowany kod HTML wpisu w blogu blogHTML += "" + this.date.getDate() + "/" + (this.date.get Month() + 1) + "/" + this.date.getFullYear() + "
" + this.body + "

"; return blogHTML; Każda

metoda jest tworzona jako obiektu prototype, a nie jako metowłaściwość da obiektu określana w jego konstruktorze przy użyciu słowa kluczowego this.

// Sprawdza, czy treść blogu zawiera podany łańcuch znaków Blog.prototype.containsText = function(text) { return (this.body.toLowerCase().indexOf(text.toLowerCase()) != -1); };

Bardziej efektywna strona MagicznaKostka Teraz strona MagicznaKostka używa już metod należących do klasy, a zatem — dzięki zastosowaniu prototypu — Renia uniknęła niepotrzebnego powielania kodu. Niezależnie od tego, jak dużo obiektów Blog stworzymy, to i tak będzie istnieć tylko jedna kopia każdej z jego metod. A wszystko dzięki temu, że metody te należą do klasy. Najciekawsze jest jednak to, że z punktu widzenia skryptów korzystających z obiektów Blog na stronie MagicznaKostka najzupełniej nic się nie zmieniło. Klasa Blog.

Instancje obiektu Blog wywołujące metody klasy.

alert(blog[0]);

Blog toString()

blog[2].toHTML();

toHTML() containsText()

470

Rozdział 10.

blog[3].containsText("kostka");

Tworzenie własnych obiektów Kluczowe zagadnienia 



Klasa jest opisem obiektu, natomiast instancje to rzeczywiste obiekty utworzone na podstawie tego opisu. Klasa określa, jakie właściwości i metody będzie mieć obiekt, natomiast instancja określa rzeczywiste wartości tych właściwości, czyli udostępnia dane, na których mogą operować metody.





Słowo kluczowe this zapewnia możliwość odwoływania się do instancji obiektu z poziomu jego własnego kodu. Obiekt prototype pozwala na zapisywanie metod w klasie, dzięki czemu można uniknąć niepotrzebnego powielania ich kodu w instancjach obiektu.

Nie ma

niemądrych pytań

P: Wciąż nie do końca rozumiem

różnicę między klasami i instancjami obiektów. O co w tym chodzi?

O: Klasy mają na celu ułatwienie tworzenia

i wielokrotnego stosowania obiektów. Nic nie stoi na przeszkodzie, byś tworzył obiekty, używając w tym celu literałów. Jedynym problemem będzie niepotrzebny wysiłek, jaki w to włożysz. Takie rozwiązanie jest nieefektywne, gdyż wymaga od ciebie większego nakładu pracy. Można by to porównać do sytuacji, gdy architekt każe rysować plany od nowa za każdym razem, gdy chce wybudować dom według tego samego projektu. A dlaczego nie można by stworzyć szablonu, na podstawie którego później tworzylibyśmy tyle obiektów, ile zechcemy? W ten sposób ograniczylibyśmy nakład pracy koniecznej do ich stworzenia. I to właśnie w tym miejscu na scenę wkraczają klasy — możemy utworzyć klasę, a następnie użyć jej do tworzenia tylu instancji obiektu, ilu nam potrzeba.

P: No dobrze, czyli klasy mają

za zadanie ułatwić tworzenie podobnych do siebie obiektów. A co wspólnego mają z tym this i prototype?

O: Słowo kluczowe this zapewnia dostęp do instancji obiektu z poziomu jego metod. Najczęściej jest ono używane podczas korzystania z właściwości obiektów. Jeśli zatem w kodzie jakiejś metody chcesz skorzystać z właściwości o nazwie x, to możesz to zrobić, używając wyrażenia this.x. Gdybyś użył samej nazwy właściwości (x), to interpreter JavaScriptu

nie wiedziałby, że chodzi ci o pobranie lub określenie wartości właściwości, i uznałby, że chcesz odwołać się do zmiennej lokalnej o nazwie x. To właśnie z tego powodu konieczne jest stosowanie słowa kluczowego this podczas tworzenia i inicjalizacji właściwości w konstruktorach obiektów. Obiekt prototype to coś całkowicie innego. Udostępnia on mechanizm pozwalający na tworzenie klas. JavaScript, w odróżnieniu od wielu innych języków programowania, takich jak C++ lub Java, nie obsługuje tak naprawdę klas jako odrębnego elementu języka. W JavaScripcie klasy są jedynie symulowane, a w tym celu używany jest właśnie prototyp, czyli obiekt prototype. Efekt końcowy jest co prawda podobny, jednak w JavaScripcie tworzenie i określanie struktury „klas” odbywa się właśnie za pomocą obiektów prototype. Każdy obiekt tworzony w języku JavaScript zawiera obiekt prototype, który jest dostępny jako „ukryta” właściwość o tej samej nazwie. Zapisując właściwość lub metodę w obiekcie prototypu, zapewniamy, że będzie ona dostępna jako element klasy, a nie jedynie konkretnej instancji obiektu.

P: A jaki jest związek konstruktora i klasy?

O: Konstruktor jest niezwykle ważnym

elementem związanym z tworzeniem klas w JavaScripcie, gdyż odpowiada za tworzenie nowych instancji obiektów. Możesz wyobrazić sobie, że konstruktor i prototyp to dwa podstawowe elementy układanki, jaką jest klasa. Konstruktor odpowiada za przygotowanie wszystkiego na poziomie instancji obiektu, natomiast prototyp — na poziomie klasy.

Oba te elementy współpracują ze sobą, by zapewnić nam możliwość wykonywania naprawdę ciekawych rzeczy, a wszystko dlatego, że istnieją ważne powody, by niektóre składowe obiektów tworzyć na poziomie instancji, a inne na poziomie klas. W dalszej części rozdziału będziemy bardziej szczegółowo zajmować się tymi zagadnieniami.

P: Mam problemy z konwencjami nazewniczymi stosowanymi w obiektach. Czasami ich nazwy są pisane wielką literą, czasami stosowana jest konwencja lowerCamelCase. Czy jest tu używana jakaś zasada, o której nie wiem?

O: Jedyna zasada jest taka, że nazwy klas

są zapisywane wielką literą, natomiast nazwy instancji zapisuje się zgodnie z konwencją lowerCamelCase. Dzieje się tak z tej prostej przyczyny, że nazwy instancji to nic innego jak zwyczajne zmienne, a identyfikatory zmiennych zapisujemy zgodnie z konwencją lowerCamelCase. Ta niekonsekwencja może wynikać z faktu, że terminu „obiekt” nie zawsze używaliśmy w ścisły i jednoznaczny sposób. A zatem — dla zachowania precyzji — nazwy klas, takie jak Blog, należy zapisywać wielką literą, natomiast nazwy instancji, takie jak blogEntry, zgodnie z konwencją lowerCamelCase. Takie rozwiązanie ma sens, zwłaszcza jeśli przyjrzymy się innym standardowym obiektom, które poznaliśmy we wcześniejszej części książki. A zatem aktualną datę i godzinę można zapisać w zmiennej (instancji) o nazwie teraz, utworzonej jak obiekt (klasa) Date.

jesteś tutaj  471

Proszę o podpis tutaj… i tutaj… i tutaj

Podpisywanie wpisów w blogu Renia pracuje nad poprawą wydajności działania strony oraz organizacji kodu, które zostały uzyskane dzięki zastosowaniu obiektowych technik programowania. Jednak nie interesują jej jedynie poprawki w kodzie, których nikt nie zauważy — Renia chciałaby dodać do swojej strony jakieś nowe możliwości.

Naprawdę bardzo bym chciała dodać podpis, który byłby wyświetlany pod każdym wpisem w blogu… o tutaj!

WYTĘŻ

Renia doszła do wniosku, że jej problem mogłoby rozwiązać stworzenie w klasie Blog nowej właściwości —signature. Wartość tej właściwości mogłaby być określana w konstruktorze i prezentowana w momencie wyświetlania każdego wpisu w blogu… i po problemie!

umysł

Czy Renia powinna utworzyć podpis jako właściwość składową określaną dla każdej instancji obiektu? Czy jesteś w stanie podać jakiś powód uzasadniający, że nie jest to najlepsze rozwiązanie?

472

Rozdział 10.

Tworzenie własnych obiektów

Nie ma

niemądrych pytań

P: Wciąż spotykam się

z określeniem „obiektowy”. Co ono oznacza?

O

: W kręgach osób związanych z programowaniem termin „obiektowy” jest używany (i nadużywany) bardzo często, a co więcej, dla różnych osób może znaczyć coś innego. Ogólnie rzecz biorąc, termin „programowanie obiektowe” (ang. Object-Oriented Programming, w skrócie

We wszystkich wpisach podpis jest taki sam.

OOP) oznacza tworzenie programów w oparciu o obiekty; na takiej zasadzie kod strony MagicznaKostka korzysta z obiektów Date. Wielu programistów uważa, że programowanie obiektowe wiąże się z powszechnym stosowaniem obiektów w kodzie pisanych programów. Teoretycznie rzecz biorąc, prawdziwie obiektowy program stanowi kolekcję wzajemnie ze sobą współpracujących obiektów.

Można spotkać osoby (swoistych obiektowych purystów), które uważają, że JavaScript nie jest prawdziwym obiektowym językiem programowania. Sugeruję, byś nie marnował energii i nie wdawał się z nimi w dyskusje. Można podać rozsądne i ważne argumenty zarówno potwierdzające przynależność JavaScriptu do rodziny języków obiektowych, jak i przeczące tej klasyfikacji. Nie warto się spierać, bo pod koniec dnia i tak nikt nie wygra.

Chwileczkę! Czy Renia nie ma tylko jednego podpisu? Skoro ma, to dlaczego każdy egzemplarz obiektu ma dysponować własną właściwością zawierającą podpis?

14/08/ 2008.............................. .................... .......... .................................................. .......... Dostała m właśni .................... e .......... nową, .......... zamów.......... .......... ioną układa .......... nkę. .......... ........................................ To prawdz .................... cudeńk o. .............................. ..........iwe .......... .......... ........................................ napisał a Renia .................... – .......... miłośn.......... iczka układa .......... .......... ..........nek .................................................. .................................................. .................................................. .................... 19/08/ 2008.............................. .................... .......... .................................................. .......... Udało mi się.......... .................... już ułożyć .......... nową układa nkę. ..........tę ..............................Już mnie.......... znudzi ła, .......... .......... .......... więc.......... szukam .................... czegoś nowego . .............................. .............................. .............................. napisał a Renia .................... – .......... miłośn.......... iczka układa .......... .......... ..........nek .................................................. .................................................. .................................................. .................... 16/08/ 2008.............................. .................... .......... .................................................. .......... Już mnie głowa .................... rozbola ła.......... od zabaw .............................. tą .......... nową kostką. .......... ........................................ Muszę sobie.......... odpoczą .................... ć. .............................. .......... .......... ........................................ napisał a Renia .................... – .......... miłośn.......... iczka układa .......... .......... ..........nek .................................................. .................................................. .................................................. .................... 21/08/ 2008.............................. .................... .......... .................................................. .......... Znalaz łam.......... w internec .................... ie układa .......... nkę 7×7×7 .......... . O rany! Zabaw ........................................ będzie.......... ..........a.......... super............................... .................... .................................................. .................... napisał a Renia .................... – .......... miłośn.......... iczka układa .......... .......... ..........nek .................................................. .................................................. .................................................. .................... 29/08/ 2008.............................. .................... .......... .................................................. .......... Spotkałam się .................... z kilkom a innymi .............................. zapaleń.......... cami, .......... by porozm .................... awiać o .......... .......... .......... układa niu kostki .............................. I szczerz ..........7×7×7 e mówią ........... .......... c, mam mies .......... zane .......... uczucia. .......... .......... .......... .......... napisał a Renia .................... – .......... miłośn.......... iczka układa .......... .......... ..........nek ..................................................

A może jeden podpis wystarczy? Skoro wiemy, że Renia ma tylko jeden podpis, nietrudno jest się domyślić, że nie ma większego sensu, by każda instancja obiektu Blog posiadała własną właściwość signature. A zatem Renia potrzebuje właściwości klasowej — właściwości, która jest tworzona tylko raz i jest przechowywana w klasie, a nie w poszczególnych instancjach obiektów. Blog Właściwość signature powinna być przechowywana w klasie Blog, a nie w instancjach obiektów.

signature

jesteś tutaj  473

Zapisz raz, korzystaj do woli…

Także właściwości klasowe są współdzielone Właściwości klasowe są bardzo podobne do metod należących do klasy — także w ich przypadku właściwość należy do klasy, jest tworzona tylko raz, a wszystkie obiekty danej klasy mają do niej dostęp. Pod pewnym względem tworzenie tylko jednej właściwości ma większe znacznie niż tworzenie jednej metody, gdyż oznacza to, że będzie istnieć tylko jedna wartość, a dostęp do niej będą miały wszystkie obiekty. W przypadku właściwości signature to jest dokładnie to, o co chodziło Reni, gdyż w całym blogu używa ona tylko jednego podpisu. Blog Jeśli zmienisz właściwość Blog.signature, to zmiany te będą miały wpływ na wszystkie instancje obiektu Blog.

Blog Znalazłam w internecie układankę 7×7×7. O rany! Zabawa będzie...

owa Już mnie gł rozbolała nową od zabaw tą kostką... 16 sierpnia

signature = "Renia – miłośniczka układanek";

Właściwość klasowa signature jest przechowywana jedynie w klasie.

Właściwość klasowa jest zapisywana tylko raz, choć wszystkie obiekty mają do niej dostęp.

Jeśli zmienisz wartość właściwości date tego obiektu, to zmiana będzie miała wpływ jedynie na ten obiekt.

umysł

Jak sądzisz, w jaki sposób można utworzyć właściwość klasową?

Rozdział 10.

Blog Spotkałam się z kilkoma inn ymi zapaleńcami , by porozmaw iać o układaniu ... 29 sierpnia 2008

Choć właściwość signature jest przechowywana w klasie Blog, to jednak wszystkie obiekty, które potrzebują podpisu autorki, mają do niej dostęp.

WYTĘŻ

474

2008

Blog

21 sierpnia 2008

Jeśli zmienisz zawartość właściwości body tego wpisu, to nie będzie to miało wpływu na pozostałe wpisy w blogu.

Każda instancja sama przechowuje swoje właściwości składowe.

Tworzenie własnych obiektów

Tworzenie właściwości klasowych przy użyciu prototypu Wziąwszy pod uwagę miejsce przechowywania właściwości klasowych oraz jego wpływ na naszą egzystencję, trzeba zauważyć, że tworzenie właściwości klasowych jest zaskakująco nieciekawe. Okazuje się, że zajmuje ono zaledwie jeden wiersz kodu:

W odwołaniach do właściwości klasowych używany jest operator kropki.

Blog.prototype.signature = "Renia – miłośniczka układanek"; W pierwszej kolejności podaje się nazwę klasy (w naszym przypadku Blog), a po niej obiekt prototype.

Nie trzeba inicjować wartości właściwości klasowych, jednak , gdyż w naszym przypadku ma to sens od podpis autorki blogu jest znany momentu wyświetlenia strony.

Zapewne sam się domyśliłeś, że właściwości kla są zapisywane w sowe obiekcie prototype.

Jedną z najbardziej interesujących rzeczy zawiązanych z powyższym fragmentem kodu jest coś, czego nie możesz zauważyć, patrząc na niego. Chodzi mianowicie o to, że powyższy kod nie jest umieszczony w konstruktorze, jak kod inicjujący właściwości składowe. Wynika to z faktu, że konstruktory służą do tworzenia instancji obiektów i z tego powodu nie są w stanie tworzyć właściwości klasowych. Okazuje się, że właściwości klasowe muszą być tworzone poza konstruktorem.

e Właściwości składow

Właściwość klasowa Blog.prototype.signature = "Renia – miłośniczka układanek";

Właściwości klasowe są tworzone poza konstruktorem obiektu i wymagają nieznacznej pomocy ze strony ukrytej właściwości prototype.

date) { function Blog(body, y; this.body = bod this.date = date; }

Jedna kopia właściwości signature, dostępna dla wszystkich obiektów Blog.

Właściwości składowe są tworzone w konstruktorze obiektu.

Każda instancja obiektu Blog posiada swoją kopię właściwości body i date.

Zaostrz ołówek Poniżej zapisz kod, który wyświetli wartość właściwości signature w informacyjnym okienku dialogowym. Podpowiedź: załóż, że kod ten będzie wykonywany w jednej z metod obiektu Blog. ........................................................................................................................................................

jesteś tutaj  475

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie

Poniżej zapisz kod, który wyświetli wartość właściwości signature w informacyjnym okienku dialogowym. Podpowiedź: załóż, że kod ten będzie wykonywany w jednej z metod obiektu Blog. Odwołania do właściwości klasowych alert(this.signature);

niczym się nie różnią od odwołań do normalnych właściwości obiektów — także w nich jest używane słowo kluczowe this.

...........................................................................................................................................

Nie ma

P: Dlaczego w ogóle

przechowujecie podpis we właściwości? Czy nie można by go wpisywać w treści każdego wpisu?

O: Bez wątpienia można by przechowywać podpis jako część treści każdego wpisu, jednak to wymagałoby dodatkowego czasu i zachodu, a także założenia, że jest tylko jedna osoba pisząca blog. Własnoręczne podpisywanie każdego wpisu byłoby zapewne męczące dla Reni; a przecież JavaScript udostępnia znacznie lepszy sposób obsługi podpisu. A poza tym, kto powiedział, że Renia nie mogłaby zrobić jakiegoś prostego błędu typu: „Renia — miłośniczka zakładanek”? I jak by to wyglądało! Innym rozsądnym rozwiązaniem tego zagadnienia byłoby zapisanie podpisu Reni w postaci literału łańcuchowego i dodawanie go do treści każdego z wpisów podczas generowania kodu HTML. Takie rozwiązanie będzie działać bardzo dobrze, lecz ma jedną wadę — ukrywa ważny element danych (podpis autorki blogu) w kodzie metody formatującej, gdzie jest trudny do odnalezienia i utrzymania. Z kolei umieszczając podpis we właściwości klasowej, sprawiam, że będzie łatwo dostępny, a dzięki temu autorka blogu łatwiej go będzie mogła odszukać i w razie konieczności zmienić.

P: Jak trzeba by zmienić

tworzenie wpisu, gdyby podpis był przechowywany we właściwości składowej?

476

Rozdział 10.

niemądrych pytań

O: Pamiętaj, że każda instancja obiektu

posiada własne właściwości składowe, które są inicjalizowane w konstruktorze. Gdyby właściwość signature była właśnie taką właściwością, to konstruktor Blog() musiałby ją tworzyć i określać jej wartość dla każdej instancji. Zapewne z punktu widzenia programowania nie byłby to wielki problem, gdyż wystarczyłoby, by konstruktor zapisał we właściwości łańcuch znaków zawierający podpis. Jednak oznaczałoby to, że w rzeczywistości powstanie tyle kopii podpisu, ile będzie obiektów Blog, a to byłoby niepotrzebnym marnotrawstwem. Co więcej, oznaczałoby to, że można zmienić podpis w jednym wpisie niezależnie od pozostałych.

P: A zatem, gdybym

chciał zmodyfikować stronę MagicznaKostka tak, by mogła obsługiwać więcej osób dodających wpisy do blogu, to powinienem utworzyć podpis jako właściwość składową?

O: Tak. I w takim przypadku byłoby to

rozwiązanie właściwe, gdyż zwróć uwagę, że z założenia właściwość ta powinna mieć możliwość przechowywania różnych wartości w różnych instancjach obiektu Blog. Najprawdopodobniej w takiej sytuacji najlepszym rozwiązaniem byłoby dodanie do konstruktora Blog()kolejnego argumentu, który pozwalałby na określenie łańcucha znaków zawierającego podpis. Łańcucha tego można by następnie użyć do zainicjowania właściwości signature. Innymi słowy, właściwość signature byłaby obsługiwana dokładnie tak samo jak dwie pozostałe właściwości obiektu Blog.

P: Odnoszę wrażenie, że

właściwości klasowe działają podobnie do zmiennych globalnych. Jakie są różnice pomiędzy nimi?

O: W rzeczywistości właściwości klasowe są

bardzo podobne do zmiennych globalnych, gdyż można do nich uzyskać dostęp niemal z dowolnego miejsca skryptu. Podobny jest także sposób tworzenia właściwości klasowych i zmiennych globalnych, a przynajmniej pod względem miejsca, w jakim są one tworzone — na głównym poziomie skryptu poza kodem funkcji i metod. Właściwości klasowe różnią się natomiast od zmiennych globalnych tym, że są ściśle związane z klasą, a tym samym z instancjami obiektów. A to oznacza, że dostęp do właściwości składowych zawsze odbywa się za pośrednictwem instancji obiektów.

P: Zaraz, chwileczkę. Czy dostęp do właściwości klasowych musi odbywać się za pośrednictwem obiektów?

O: Choć właściwości klasowe są tworzone

przy użyciu obiektu prototype (który umożliwia zapisanie ich w klasie), to jednak korzystanie z nich w skrypcie musi się odbywać za pośrednictwem instancji. Oznacza to, że z właściwości klasowej korzystamy dokładnie tak samo jak z właściwości składowych — przy użyciu słowa kluczowego this i operatora kropki (.). Różnica sprowadza się do miejsca przechowywania wartości właściwości — w klasie (w przypadku właściwości klasowych) bądź w instancji obiektu (w przypadku właściwości składowych).

Tworzenie własnych obiektów

Podpisane i dostarczone Skoro właściwość signature została już utworzona, zainicjowana i jest gotowa do użycia, Renia może zastosować ją w skryptach na swojej stronie. Analizując kod obiektu Blog odpowiedzialny za wyświetlanie wpisów w przeglądarce, Renia doszła do wniosku, że metoda toHTML() jest odpowiednim miejscem, w którym podpis powinien zostać dodany do prezentowanego wpisu.

Teraz metoda toHTML() formatuje i wyświetla podpis jako element wpisu.

// Generujemy wpis w postaci sformatowanego kodu HTML Blog.prototype.toHTML = function(highlight) { w argumencie // Używamy szarego tła jako podświetlenia, jeśli tak zażądano var blogHTML = ""; : "

"; blogHTML += highlight ? "

" // Generujemy sformatowany kod HTML wpisu w blogu e.getMonth() + 1) + "/" + blogHTML += "" + this.date.getDate() + "/" + (this.dat "
" + this.signature + this.date.getFullYear() + "

" + this.body + "

"; return blogHTML; };

Teraz podpis Reni jest wyświetlany jako element każdego wpisu.

Teraz nie ma już żadnych niedomówień odnośnie tego, kto jest autorem poszczególnych wpisów.

Odwołanie do właściwości klasowej signature wygląda dokładnie tak samo jak odwołania do pozostałych właściwości obiektu Blog.

Renia zastosowała kolejne techniki obiektowe, by jeszcze bardziej rozszerzyć język JavaScript poprzez dodanie do obiektu Blog właściwości klasowej, zawierającej podpis autorki wpisów. Równie ważne jest jednak to, że Reni udało się nadać stronie MagicznaKostka nieco bardziej osobisty charakter.

jesteś tutaj  477

Właściwość składowa a właściwość klasowa

Pogawędki przy kominku

Temat dzisiejszej pogawędki: Właściwość klasowa i składowa dyskutują na temat posiadania danych i tajnych stowarzyszeń.

Właściwość składowa:

Właściwość klasowa:

A zatem to ty jesteś tą drugą, o której ostatnio tyle się mówi. Muszę przyznać, że w ogóle nie rozumiem, co tutaj robisz. Ja doskonale spełniam swoje zadanie — zapewniam instancjom obiektów unikalność i pozwalam im panować nad wartościami swoich właściwości.

No co ty… aż trudno mi w to uwierzyć. Mów dalej…

Ależ absolutnie w to nie wątpię. Ale czy nie pomyślałaś, że czasami instancje obiektów nie chcą zawracać sobie głowy problemem, jakim są własne dane? Dobrze, a zatem zdarzają się takie sytuacje, gdy jakieś dane są wspólne dla wszystkich instancji pewnego obiektu… No wiesz, coś jak tajne stowarzyszenie, w którym wita się tajemnym uściskiem dłoni. Każda osoba należąca do stowarzyszenia zna tajemny uścisk, lecz jest on „własnością” całego stowarzyszenia, a nie tylko jednej osoby. Jeśli któraś z członkiń stowarzyszenia opracuje własny uścisk dłoni, to wszystko popsuje. Okaże się bowiem, że jakaś inna dziewczyna zaraz będzie chciała wymyślić własny, fajniejszy uścisk i niebawem okaże się, iż jest tyle uścisków, że już nikt nie zna tego właściwego.

Uważasz zatem, że nie byłabym najlepszym miejscem do przechowywania tajemnego uścisku? Owszem. Bez obrazy, ale w takiej sytuacji członkinie klubu muszą mieć tylko jeden uścisk, bez względu na to, że każda będzie go znać. Rozumiem. A co z tajemnym hasłem? Czy mogę je przechowywać? Być może. Jeśli każda osoba ma mieć swoje własne tajemne hasło, które tylko ona powinna znać. W takim przypadku faktycznie będziesz się rewelacyjnie nadawać do przechowywania takiego hasła. Cudownie! A zatem do roboty! W takim razie zaraz powołam do życia nowe tajne stowarzyszenie i obie wybierzemy swoje tajne hasła. Ale nie znasz tajnego uścisku dłoni… I tu cię mam! Dobre! Faktycznie… No to powiedz, jaki on jest… Proszę, mówię poważnie…

478

Rozdział 10.

Tworzenie własnych obiektów

Powielanie kodu jest złe Renia z powrotem wzięła się do roboty. Jako osoba, która nigdy nie spoczywa na laurach, zdecydowała się pójść jeszcze dalej w swoich próbach poprawienia efektywności działania strony MagicznaKostka. Renia zauważyła powtarzający się kod służący do formatowania dat, który, jak przypuszcza, będzie można wyeliminować, stosując jakieś chytre sztuczki obiektowe.

Wydaje mi się, że powielanie tego kodu nie jest potrzebne. W jaki sposób mogę się go pozbyć?

MagicznaKostka - blog miłośników przestrzennych układanek <script type="text/javascript"> // Konstruktor obiektu Blog function Blog(body, date) { // Przypisanie wartości this.body = body; this.date = date; } // Generowanie tekstowej reprezentacji wpisu Blog.prototype.toString = function() { h() + 1) + "/" + return "[" + this.date.getDate() + "/" + (this.date.getMont ; this.body + " "] + ear() .getFullY this.date }; // Generowanie wpisu w postaci sformatowanego kodu HTML Blog.prototype.toHTML = function(highlight) { w argumencie // Używamy szarego tła jako podświetlenia, jeśli tak zażądano var blogHTML = ""; : "<p>"; blogHTML += highlight ? "<p style='background-color:#EEEEEE'>"<br /> <br /> Ten kod formatujący daty jest identyczny, a co za tym idzie — jest przykładem marnotrawstwa.<br /> <br /> // Generujemy sformatowany kod HTML wpisu w blogu e.getMonth() + 1) + "/" + blogHTML += "<strong>" + this.date.getDate() + "/" + (this.dat "<br /><em>" + this.signature + this.date.getFullYear() + "</strong><br />" + this.body + "</em></p>"; return blogHTML; }; // Sprawdza, czy treść blogu zawiera podany łańcuch znaków Blog.prototype.containsText = function(text) { != -1); return (this.body.toLowerCase().indexOf(text.toLowerCase()) }; // Podajemy podpis autorki wpisów "; Blog.prototype.signature = "napisała Renia - miłośniczka układanek<br /> <br /> WYTĘŻ<br /> <br /> umysł<br /> <br /> W jaki sposób można usunąć ze strony MagicznaKostka powtarzający się kod formatujący datę?<br /> <br /> jesteś tutaj  479<br /> <br /> Gdzie przeprowadzać formatowanie?<br /> <br /> Metoda formatująca daty Renia uważa, że rozsądnym rozwiązaniem problemu powielonego kodu formatującego daty będzie dodanie nowej metody do obiektu Blog. W celu umożliwienia wielokrotnego stosowania kod formatujący musi być umieszczony w funkcji lub metodzie, przy czym Renia myśli raczej o metodzie, gdyż to obiekt Blog jest odpowiedzialny za formatowanie prezentowanych wpisów, a formatowanie daty jest elementem tego zadania. Czy podejście Reni jest słuszne?<br /> <br /> Jeśli przyjmiemy, że formatowanie daty jest operacją obiektu Date, to czy umieszczenie planowanej metody w obiekcie Date nie byłoby bardziej sensownym rozwiązaniem? Czy można dodawać metody do standardowych obiektów JavaScriptu?<br /> <br /> Powrót do obiektu prototypu Czy można sobie wyobrazić mechanizm zapewniający większe możliwości niż rozbudowa i ulepszanie istniejących obiektów? Jak się okazuje, istnieje sposób pozwalający na modyfikowanie standardowych obiektów i, jak można sądzić, przełamuje on ostatnie bariery ograniczające możliwości rozszerzania języka JavaScript. Kluczem otwierającym bramy do rozszerzania standardowych obiektów, a właściwie — do wszelkich obiektów JavaScriptu jest obiekt prototype. Korzystaliśmy już z niego w celu rozbudowy klasy Blog i dodania do niej właściwości klasowej oraz metod należących do klasy. Teraz okazuje się, że nic nie stoi na przeszkodzie, by w dokładnie ten sam sposób rozszerzać możliwości wbudowanych obiektów JavaScriptu.<br /> <br /> prototype<br /> <br /> Każdy obiekt zawiera obiekt prototype, który pozwala dodawać właściwości i metody na poziomie klasy.<br /> <br /> 480<br /> <br /> Rozdział 10.<br /> <br /> Każdy istniejący obiekt, w tym także standardowy obiekt JavaScriptu.<br /> <br /> Tworzenie własnych obiektów<br /> <br /> Rozszerzanie standardowych obiektów Jeśli chodzi o rozszerzanie obiektów, to kluczowe znaczenie w jego przypadku ma obiekt prototype, który w języku JavaScript jest dostępny bez wyjątku we wszystkich obiektach. A zatem każdy obiekt w JavaScripcie można rozszerzyć, dodając nowe właściwości i metody do jego obiektu prototype. W ten sposób możemy stworzyć nowe właściwości i metody na poziomie klasy. W przypadku wbudowanych obiektów JavaScriptu dodanie właściwości lub metody do ich obiektu prototype oznacza, że każdy utworzony obiekt tego typu będzie mieć do nich dostęp.<br /> <br /> Obiekt prototype pozwala na rozszerzanie wbudowanych obiektów JavaScriptu.<br /> <br /> String prototype<br /> <br /> scramble()<br /> <br /> String<br /> <br /> Dodanie metody do prototypu wbudowanego obiektu powoduje umieszczenie jej w klasie.<br /> <br /> Tworzymy metodę jako składową obiektu prototype obiektu String.<br /> <br /> scramble() Nowe obiekty tworzone na podstawie klasy będą mogły korzystać z nowej metody.<br /> <br /> String.prototype.scramble = function() { // Zwracamy łańcuch o losowo zmienionej kolejności liter<br /> <br /> String<br /> <br /> ... „Renia zka — miłośnic układanek”<br /> <br /> }<br /> <br /> Zastosowanie nowej metody obiektu String sprowadza się do jej wywołania za pośrednictwem instancji obiektu String.<br /> <br /> String<br /> <br /> String<br /> <br /> "Zdecydowa łam "Spotkałam się się zamówi ć z kilkoma innymi tę przeraża jąc ą zapaleńcami, kostkę by porozmawiać o wymiarac h o układaniu…" 7×7×7…"<br /> <br /> alert(this.signature.scramble());<br /> <br /> Zaostrz ołówek Napisz kod metody o nazwie shortFormat(), stanowiącej rozszerzenie standardowego obiektu Date, której zadaniem będzie wyświetlenie daty w formacie DD/MM/RRRR. ................................................................................................................................................ ................................................................................................................................................ ................................................................................................................................................<br /> <br /> jesteś tutaj  481<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Metoda zostaje dodana do prototypu obiektu Date.<br /> <br /> Napisz kod metody o nazwie shortFormat(), stanowiącej rozszerzenie standardowego obiektu Date, której zadaniem będzie wyświetlenie daty w formacie DD/MM/RRRR. Date.prototype.shortFormat = function() { ................................................................................................................................................ return this.getDate() + "/" + (this.getMonth() + 1) + "/" + this.getFullYear(); ................................................................................................................................................ } ................................................................................................................................................<br /> <br /> Zmodyfikowany obiekt daty = lepsza strona Reni Zmodyfikowany obiekt Date poprawia wydajność strony MagicznaKostka i rozszerza możliwości wbudowanego obiektu JavaScriptu. Co więcej, ułatwiliśmy także utrzymanie strony, gdyż obecnie kod formatujący daty, mający wpływ na postać dat na całej stronie, jest umieszczony w jednym miejscu, gdzie łatwiej można go zmodyfikować. Oczywiście, obiektowe usprawnienia kodu skryptu nie zawsze dają skutki, które można od razu zauważyć na stronie, jednak bardzo często pozwalają tworzyć kod, który wydaje się działać lepiej, zwłaszcza w dłuższej perspektywie.<br /> <br /> Obecnie daty w blogu Reni są formatowane przez niestandardową metodę obiektu Date.<br /> <br /> 482<br /> <br /> Rozdział 10.<br /> <br /> Date prototype<br /> <br /> shortFormat()<br /> <br /> Tworzenie własnych obiektów<br /> <br /> Klasa może mieć swoją własną metodę Nasza niestandardowa metoda shortFormat() obiektu Date jest metodą składową należącą do klasy, co oznacza, że choć należy do klasy, ma dostęp do właściwości składowych obiektu. To właśnie dzięki temu metoda ta może sformatować datę przechowywaną we właściwości konkretnej instancji obiektu. Istnieje także możliwość utworzenia metody klasowej, która również należy do klasy, lecz nie ma dostępu do właściwości składowych. Takie metody klasowe mogą jednak korzystać z właściwości klasowych, takich jak signature w klasie Blog. Blog<br /> <br /> Metody klasowe należą do klasy i mają dostęp wyłącznie do właściwości klasowych.<br /> <br /> Klasa<br /> <br /> Blog<br /> <br /> signature<br /> <br /> showSignature()<br /> <br /> Metody klasowe mogą korzystać z właściwości klasowych.<br /> <br /> toString()<br /> <br /> body<br /> <br /> Metody klasowe nie mają dostępu ani do właściwości, ani do metod składowych.<br /> <br /> "Brałam udział toHTML() w wyścigu przed lokalnym sklepem z zabawkami…" 3 września 2008<br /> <br /> date<br /> <br /> Stworzenie metody klasowej sprowadza się do przypisania metody do klasy bez użycia obiektu prototype — a zatem wystarczy podać nazwę klasy i użyć notacji obiektowej.<br /> <br /> Blog.showSignature = function() { <br /> <br /> containsText()<br /> <br /> Instancja<br /> <br /> Aby odwołać się do właściwości klasowej z poziomu metody klasowej, musimy skorzystać z właściwości prototype.<br /> <br /> alert("Ten wpis został napisany przez "+ Blog.prototype.signature + "."); } <br /> <br /> Ponieważ metody klasowe nie są w żaden sposób skojarzone z instancjami obiektów, zatem można się do nich odwołać wyłącznie poprzez użycie nazwy klasy. Nie oznacza to wcale, że instancje obiektu nie mogą wywoływać tych metod — mogą, lecz muszą to robić za pośrednictwem nazwy klasy. Blog.showSignature();<br /> <br /> W wywołaniu metody klasowej kluczowym elementem jest nazwa klasy.<br /> <br /> Ponieważ signature jest właściwością klasową, zatem nasza metoda klasowa ma do niej dostęp.<br /> <br /> WYTĘŻ umysł<br /> <br /> Czy potrafisz wskazać jakiś fragment kodu strony MagicznaKostka, który warto by umieścić w metodzie klasowej?<br /> <br /> jesteś tutaj  483<br /> <br /> Wróćmy do klasy Blog<br /> <br /> Czy można by użyć metody klasowej jako narzędzia do określania kolejności sortowania wpisów w blogu?<br /> <br /> ............ ............... ............ .................. ............... ............................................. ............ ......... ......... ......... ............... ............ ......... ............... ......... ............ ............................................. ......... ............... ............ ......... ............... ... ............ ... ............ ......... ......... 01/0 ...... ......... 9/20 01/09 ...... ......... 08 /2008 ...... .................. ............... .................. ............ ......... ......... /2008 ......... ......... 01/09 ............... ......... ............ ......... ......... ......... ............... ......... ............ .................. ......... ............... ............ ............... ... Już ...... 7.......... ............... ...... 7×7× ......... kostkę ......... Zdec żającą ...... ......... ydow przera tę Zdecy ...... ......... ić ałam dował ...... ......... .................. zamów am się ...... ......... się się am zam ...... zamów ......... ......... dował ówić ...... ......... ić......... ......... Zdecy tę przera ...... ......... tę prze żającą ...... .................. ......... raża ...... ......... ...... 7×7× ......... ......kostkę kost7.kę .................. ......... Już ......jącą 7×7 ...... ......... ×7. ...... ......... Już ...... ......... ...... .................. ...... ......... układ jej......... zacz ć na ...... ........................ ynam owywa zaczyn ......am .........anie. przygot się ...... ......... wo .................. się duch ...... ......... ducho ducho się owo ...... ......... wo ......... am przygot ...... przy ......... ......... zaczyn owywa ...... ......... got ......... owyw ...... ć na .................. jej...... ...... ać układ na ...... anie. ......... jej ...... .................................... ......... ukła ......... dan ...... ......... ie.......... ...... ......... ...... ...... .................. ...... ...... ......... ............... ............ .................. ............... ............................................. ...... ......... ...... ......... ......... ............... ......... ...... ......... ...... ............... ......... ............ .................................... ......... ............... ............ ......... ......... ...... ...... ...... ...... ......... ............ 03/0 ...... .................. 9/20 ...... 03/09 ......... 08 /2008 ...... ......... .................. ...... ......... ...... ......... ......... /2008 ...... ......... ......... 03/09 ............... ......... ......... ............ ......... ......... ......... ............... ......... ............ .................. ......... ............... ......... ...... ............... ...... kami, ...... zabaw ...... m z...... ......... ............ sklepe ......... Brał ym......... ...... ......... am lokaln ............ Brałam ......... udz przed u iał udzia .................. wyścig ...... w w ......... ł ł w wyśc wyścig ...... ......... udzia ......... igu u ...... ......... przed ......... prze Brałam ...... ......... lokaln ......... d ...... loka ......... ym .................. sklepe ...... lnym m...... ............... z ......... skle ......... zabaw ...... ........................... pem kami, ...... ......... z ...... zaba ...... wka ......... ...... mi, moc ... ... —......... ...... anek ...... układ ......... ............ icy......... ......... któr Miłośn ...... ......... anki. przes ...... który układ przest tał ...... ......... awać .................. sprz ałytał ...... ......... sprzed sprzed edaw ...... ......... awać ......... przes ać ...... ......... układ ......... który ukła ...... ......... anki. ......... dan ...... Miłośn ......... ......... ki....... ...... ......... icy......... ......... Miło układ ...... ......... śnic anek ...... .................. ......... y ...... — ukła moc ...... dan ......... ...... ek ......... — ......... ...... ...moc ...... ... ...... ...... ...... jest ...... ........................... z nam ...... jestjes ......... zt......... i! ...... nami! ......... .................. ...... ......... ...... ......... ......... nami! ...... z ......... ......... ...... ......... ......... ............ ......... ......... ......... ............... ......... ............ .................. ......... ............... ............ ......... ......... ...... ... ...... ...... ...... ... ...... ......... ......... ...... ......... ............ ........................... ............... ........................... ...... ......... ...... ......... ......... ...... ......... ...... ......... ...... ......... ............... ......... ............ .................................... ......... ............... ............ ......... ......... ...... ... ...... ...... ...... ... ...... ......... ......... 05/0 ...... ......... 9/20 ...... 05/09 ......... 08 ...... /2008 ......... .................. ...... ......... ............... ......... /2008 ...... ......... ......... 05/09 ...... ......... ............... ......... ......... ...... ......... ......... ............... ......... ............ .................. ......... ............... ...... ......... ...... ......... ...... e będę ... ...... ...... pewni ...... ... ...... czas jakiś ......... przez ......... Dost — ...... 7 ......... ałam ......am 7×7× Dostał ......... zam ...... kostkę ......... .................. zamów ówio ioną ...... ......... ioną ną ...... zamów ......... ......... kostkę kost am ...... ......... ......... kę Dostał 7×7× ............... ......... 7×7......... ......... 7...... ...... — ......... ×7...... ......... przez ...... — ......... jakiś przeczas ......... ...... z jaki .................. pewni e będę ś czas ......... ...... ......... ...... pew......... ......... ...... nie ......... ...... ...będę ...... ...... ... ...... ...... ......... milc ...... ......... zeć. ............ milczeć . ......... .................. ...... ........................... ...... ......... . ......... ......... ...... ......... milczeć ...... ......... ......... ...... ......... ...... ......... ......... ............ ......... .................. ......... ...... ......... ............ ......... ......... ...... ......... ...... ......... ...... ...... ......... ......... ......... ............ ......... ......... ...... ......... .................. ...... .................. ........................ ...... ......... ...... ......... ......... ...... ......... ...... ......... ............ ......... .................................... ......... ...... ......... ............ ......... ......... ...... ......... ............ ......... ......... ......... ............ ................................. ...... ......... .................. ...... ......... ...... ......... ......... ...... ......... ......... ............ ......... ......... ......... ...... ......... ............ ......... .................................... ......... ...... ......... ............ ......... ......... ...... ......... ...... ......... ...... ...... ......... ......... ......... ...... ......... ...... ........................ ............ .................................... ......... ...... ......... ......... ...... ......... ......... ...... ......... ...... ......... ............ ......... .................................... ......... ...... ......... ............ ......... ......... ...... ......... ............... ............ ...... ...... ......... ...... .............................. ......... ......... ......... .................. ......... ......... ........................... ...... ............... ......... ...... ......... ...... ......... ............ ......... .................................... ......... ...... ......... ............ ......... ......... ...... ......... ............ ............ ...... ......... ...... .............................. ........................... ......... .................. ......... ......... .................. ...... ......... ......... ...... .................. ......... ............... ...... ......... ............ ......... .................. ......... ...... ......... ............ ......... ......... ...... ......... ............ ............ ...... ......... ...... .............................. .................. ............................................. ......... ........................... ...... ......... ............ ......... ......... ...... ......... ............ ......... .................................... ......... ...... ......... ............ ......... ......... ...... ......... ............ ............... ...... ... ......... ......... .............................. ......... ......... ......... .................. ......... ......... .................. .................. ......... ...... ......... ............ ......... ............................................. ......... ...... ......... ............ ......... ......... ...... ......... ............ ... ...... ... ............ ......... .............................. ........................... ......... .................. ......... ......... .................. ...... ......... ...... ......... ...... ...... ......... ............ ......... .................................... ......... ...... ......... ............ ......... ......... ...... ......... ............ ... ...... ... ...... ......... ...... ......... ...... ......... ...... ......... ...... ........................... ............ .................. ......... ...... ......... ......... ............ ......... ......... ......... ...... ......... ............ ......... .................................... ......... ...... ......... ............ ......... ......... ...... ......... ............ ... ...... ... ............ ......... ......... .............................. ......... ......... ......... .................. ......... ......... .................. ......... ...... ......... ............ ......... ......... ...... ......... ............ ......... .................................... ......... ...... ......... ............ ......... ......... ...... ......... ............ ... ...... ... ............ ......... .............................. ........................... ......... .................. ......... ......... .................. ...... ......... ...... ......... ......... ............... ...... ......... ............ ......... .................................... ......... ...... ......... ............ ......... ......... ...... ......... ............ ............... ......<br /> <br /> Ponowna analiza sposobu sortowania wpisów<br /> <br /> To bardzo interesujący pomysł, gdyż funkcja porównująca wykorzystywana podczas sortowania wpisów jest bez wątpienia powiązana logicznie z obiektem Blog. Aktualnie jest ona tworzona jako literał funkcyjny wewnątrz funkcji showBlog(), czyli dokładnie tam, gdzie jest potrzebna. function showBlog(numEntries) { // W pierwszej kolejności sortujemy blog w odwrotnej kolejności chronologicznej // (najnowszy na początku) blog.sort(function(blog1, blog2) { return blog2.date - blog1.date; } ); ... } Sortowanie wpisów w blogu jest obsługiwane wewnątrz funkcji showBlog(), która nie jest częścią obiektu Blog.<br /> <br /> 484<br /> <br /> Rozdział 10.<br /> <br /> Chyba będzie można przenieść kod określający porządek sortowania do metody klasowej.<br /> <br /> Jednym z podstawowych pojęć programowania obiektowego jest hermetyzacja. Zaleca ona, by cała funkcjonalność powiązana z obiektem została zaimplementowana wewnątrz niego. Innymi słowy, kod umieszczony poza obiektem nie powinien robić z nim niczego, co obiekt może wykonać we własnym zakresie. W naszym przypadku porównywanie wpisów w celu określenia porządku sortowania mogłoby być wykonywane wewnątrz obiektu, a nie w kodzie funkcji showBlog(). Powstaje jednak pytanie, czy kod porównujący wpisy można umieścić w metodzie klasowej w klasie Blog. Aby odpowiedzieć na to pytanie, musimy sprawdzić, czy metoda ta potrzebuje dostępu do właściwości lub metod składowych. Gdyby potrzebowała, to mielibyśmy problem, gdyż metody klasowe nie mają dostępu do niczego, co jest przechowywane w instancjach obiektów.<br /> <br /> Tworzenie własnych obiektów<br /> <br /> Analiza funkcji porównującej wpisy Jedynym sposobem, by przekonać się, czy rozwiązanie, o którym wspominaliśmy wcześniej, będzie działać, czy nie, jest zajrzenie do kodu funkcji i zobaczenie, co się w nim dzieje. Poniżej przedstawiliśmy zatem literał funkcyjny odpowiadający za określanie porządku sortowania wpisów, zapisany w sposób typowy dla funkcji: Do funkcji przekazywane są dwie instancje obiektu Blog.<br /> <br /> function(blog1, blog2) { return blog2.date – blog1.date; }<br /> <br /> Porównanie obu wpisów w celu określenia ich porządku wymaga obliczenia różnicy ich dat.<br /> <br /> Choć powyższa funkcja operuje bezpośrednio na instancjach obiektów Blog, to jednak są one przekazywane do niej jako argumenty wywołania. To zupełnie coś innego niż próba pobrania właściwości lub wywołania metody wewnątrz metody obiektu przy użyciu słowa kluczowego this, co — jak wiemy — nie jest możliwe w kodzie metod klasowych. A zatem, jak się okazuje, funkcja porównująca nie musi odwoływać się do żadnych właściwości wewnątrz instancji obiektu, czyli nic nie stoi na przeszkodzie, by zaimplementować ją jako metodę klasową. W rzeczywistości funkcja porównująca nawet nie musi korzystać z właściwości klasowych, choć w razie konieczności mogłaby to robić (gdyż — jak wiemy — metody klasowe mają dostęp do właściwości klasowych). Metoda klasowa ma dostęp do właściwości klasowych.<br /> <br /> Blog<br /> <br /> signature<br /> <br /> Klasa<br /> <br /> Funkcja porównująca wpisy nie musi mieć dostępu ani do właściwości klasowych, ani do właściwości składowych. function(blog1, blog2) { return blog2.date – blog1.date; }<br /> <br /> Instancja<br /> <br /> Blog body<br /> <br /> toString()<br /> <br /> Dostałam toHTML() zamówioną kostkę 7×7×7 — przez jakiś czas pewnie będę milczeć.<br /> <br /> date<br /> <br /> 5 września 2008<br /> <br /> containsText()<br /> <br /> Gdyby funkcja porównująca potrzebowała dostępu do danych instancji obiektu, to nie można by jej zaimplementować jako funkcji klasowej.<br /> <br /> Zaostrz ołówek Zmodyfikuj kod funkcji porównującej wpisy, używanej na stronie MagicznaKostka, i zaimplementuj ją jako metodę klasową klasy Blog. Metodzie nadaj nazwę blogSorter(). ................................................................................................................................................ ................................................................................................................................................ ................................................................................................................................................<br /> <br /> jesteś tutaj  485<br /> <br /> Metoda klasowa? Proszę tam…<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Zmodyfikuj kod funkcji porównującej wpisy, używanej na stronie MagicznaKostka, i zaimplementuj ją jako metodę klasową klasy Blog. Metodzie nadaj nazwę blogSorter(). Blog.blogSorter = function(blog1, blog2) { ................................................................................................................................................<br /> <br /> ąca Teraz metoda porównują tod me się ła sta isy wp klasową klasy Blog o nazwie blogSorter().<br /> <br /> return blog2.date – blog1.date; ................................................................................................................................................ } ................................................................................................................................................<br /> <br /> Wywoływanie metody klasowej Korzyści z zaimplementowania funkcji porównującej jako metody klasowej stają się wyraźniej zauważalne, kiedy spojrzymy na kod, w którym metoda ta jest wywoływana. function showBlog(numEntries) { // W pierwszej kolejności sortujemy blog w odwrotnej kolejności chronologicznej // (najnowszy na początku) blog.sort(Blog.blogSorter); ... }<br /> <br /> Teraz wszelkie szczeg óły operacji sortowania wpisów w przekazane do metody blogu zostały — metody klasowej klablogSorter() sy Blog.<br /> <br /> Piękno powyższego kodu jest subtelne, lecz jednocześnie ważne. Funkcja showBlog() nie musi już zwracać uwagi na to, w jaki sposób należy sortować wpisy w blogu. Teraz wszelkie szczegóły związane z operacją sortowania zostały umieszczone w klasie Blog, czyli tam, gdzie powinien być umieszczony cały kod operujący na wpisach w blogu. Warto także zwrócić uwagę na to, że sortowanie wciąż jest inicjowane w funkcji showBlog(), czyli poza klasą Blog. Ma to sens, gdyż sortowanie ma wpływ na całą kolekcję instancji obiektów Blog. Jednak szczegóły porównywania ze sobą wpisów są czymś, co powinna obsługiwać sama klasa Blog. Dobry projekt obiektowy często wymaga bardzo uważnego wyreżyserowania obiektów oraz kodu, z jakim one współpracują. Blog<br /> <br /> blogSorter()<br /> <br /> showBlog()<br /> <br /> 486<br /> <br /> Rozdział 10.<br /> <br /> Jeden obraz jest wart tysiąca słów<br /> <br /> w blogu<br /> <br /> Tworzenie własnych obiektów<br /> <br /> Renia wciąż nie przestaje się fascynować obiektowymi poprawkami na stronie MagicznaKostka, jednak zdaje sobie sprawę z tego, że użytkownicy strony niekoniecznie muszą podzielać jej entuzjazm, gdyż usprawniania nie wywarły wielkiego wpływu na wygląd strony czy na sposób korzystania z niej. Dlatego też Renia doszła do wniosku, że należy dodać do strony coś, czego na pewno nie przeoczą.<br /> <br /> Obecna postać strony jest w porządku, jednak czasami chciałabym umieszczać we wpisach obrazki, którymi chcę się podzielić z innymi. Możliwość dodawania obrazków do wpisów w moim blogu byłaby naprawdę super. Sami wiecie… na pewno upiększyłoby to stronę.<br /> <br /> Pomysł Reni polega na tym, by do każdego wpisu można było dodać opcjonalny obrazek, który będzie wyświetlany wraz z treścią wpisu oraz datą. Obrazek musi być jednak opcjonalny, gdyż nie we wszystkich wpisach będzie on potrzebny. Co więcej, w ten sposób Renia nie będzie mieć problemów ze swoimi wcześniejszymi wpisami, w których obrazków nie ma.<br /> <br /> WYTĘŻ umysł<br /> <br /> W jaki sposób rozbudowałbyś obiekt Blog o możliwość obsługi opcjonalnych obrazków?<br /> <br /> jesteś tutaj  487<br /> <br /> Obrazki są najważniejsze<br /> <br /> Dodawanie obrazków do blogu Dodanie obrazków do strony MagicznaKostka wiąże się z koniecznością określenia, jak dodać do obiektu Blog obrazki w taki sposób, by nie kolidowały one z dotychczasowym sposobem działania obiektu. Próba znalezienia odpowiedzi na to pytanie prowadzi do powstania dwóch kolejnych pytań, które będą miały wpływ na postać ostatecznego rozwiązania:<br /> <br /> 1<br /> <br /> Jaki będzie najlepszy sposób przechowywania obrazków w obiekcie Blog?<br /> <br /> 2<br /> <br /> Jak dodać obrazek do wpisu, by był on całkowicie opcjonalny?<br /> <br /> Doskonale wiemy, że niezależnie od tego, jak obrazki będą przechowywane, to i tak w ostateczności trafią one do znacznika HTML <img>, dzięki któremu będą mogły zostać wyświetlone na stronie. Łańcuch znaków zawierający nazwę pliku całkowicie wystarczy do opisania obrazka.<br /> <br /> <img src=”cube777.png” /><br /> <br /> Z punktu widzenia obiektu Blog obrazek jest jedynie łańcuchem znaków.<br /> <br /> Powyższy przykład pokazuje, że z punktu widzenia wpisu w blogu obrazek może być zwyczajnym łańcuchem znaków. Oczywiście, łańcuch ten w rzeczywistości wskazuje na plik obrazka, przechowywany gdzieś na serwerze WWW, jednak z punktu widzenia obiektu Blog jest to jedynie łańcuch znaków.<br /> <br /> A zatem, aby obiekt Blog mógł obsługiwać obrazki, wystarczy dodać do niego zwyczajną właściwość, w której będzie przechowywany łańcuch znaków, podobnie jak we właściwości body.<br /> <br /> Blog body<br /> <br /> cube777.png<br /> <br /> Obrazek kostki o wymiarach 7×7×7 jest przechowywany w pliku o nazwie cube777.png.<br /> <br /> Właściwości body i image są przechowywane w obiekcie Blog i zawierają łańcuchy znaków.<br /> <br /> Dostałam toHTML() zamówioną kostkę 7×7×7 — przez jakiś czas pewnie będę date milczeć. 5 września 2008<br /> <br /> image<br /> <br /> 488<br /> <br /> Rozdział 10.<br /> <br /> toString()<br /> <br /> containsText()<br /> <br /> Tworzenie własnych obiektów<br /> <br /> Opcjonalne obrazki w blogu 2<br /> <br /> A zatem obrazek dodawany do wpisu będzie przechowywany w obiekcie Blog jako łańcuch znaków zapisany we właściwości o nazwie image. Wciąż jednak musimy znaleźć odpowiedź na pytanie, w jaki sposób sprawić, by dodawanie obrazka do wpisu było opcjonalne. Pytanie to prowadzi nas nieuchronnie do konstruktora obiektu, w którym jest on zarówno tworzony, jak i inicjowany. Bez wątpienia musi się w nim pojawić jakiś dodatkowy kod, który będzie określał, czy użyjemy właściwości image i dodamy obrazek do wpisu, czy też nie.<br /> <br /> image<br /> <br /> function Blog(body, date) { // Przypisanie wartości<br /> <br /> ?<br /> <br /> this.body = body; this.date = date;<br /> <br /> Nie jestem przekonany do takiego rozwiązania. A co się stanie, jeśli nie przekażemy argumentu w wywołaniu konstruktora? Czy w takim przypadku we właściwości nie zostanie zapisana wartość null?<br /> <br /> }<br /> <br /> Pominięte argumenty mają wartość null. Jeśli jakiś argument nie zostanie podany w wywołaniu funkcji, metody lub konstruktora, to w dowolnym kodzie, który będzie chciał z niego skorzystać, przyjmie on wartość null. W przypadku konstruktora oznacza to, że właściwość skojarzona z pominiętym argumentem także przyjmie wartość null, co — jak się okazuje — wcale nie jest złym rozwiązaniem. Cała sztuczka polega zatem na tym, by opcjonalny argument konstruktora umieścić na samym końcu listy, tak by można go było pominąć bez wpływu na pozostałe argumenty. Technikę tę można zastosować w dowolnej funkcji lub metodzie, niemniej wręcz idealnie nadaje się do zastosowania w naszym przypadku właściwości image w konstruktorze obiektu Blog.<br /> <br /> Zaostrz ołówek ................................................................................................................................................ Zmodyfikuj konstruktor obiektu Blog, tak by obsługiwał on dodatkową właściwość image, służącą do przechowywania informacji o obrazku dodanym do wpisu.<br /> <br /> ................................................................................................................................................ ................................................................................................................................................ ................................................................................................................................................ ................................................................................................................................................ ................................................................................................................................................<br /> <br /> jesteś tutaj  489<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Zmodyfikuj konstruktor obiektu Blog, tak by obsługiwał on dodatkową właściwość image, służącą do przechowywania informacji o obrazku dodanym do wpisu. function Blog(body, date, image) { ................................................................................................................................................<br /> <br /> Tu tworzymy i inicjujemy właściwość image, w której zapisywana jest wartość argumentu image.<br /> <br /> Argument image, służący do<br /> <br /> // Przypisanie wartości określenia obrazka, dodaliśmy ................................................................................................................................................ jako ostatni argument<br /> <br /> konstruktora. this.body = body; ................................................................................................................................................<br /> <br /> this.date = date; ................................................................................................................................................ this.image = image; ................................................................................................................................................ } ................................................................................................................................................<br /> <br /> Nie ma<br /> <br /> niemądrych pytań<br /> <br /> P: Czy to ważne, by argument image był umieszczony na ostatnim miejscu listy argumentów konstruktora Blog()?<br /> <br /> O: Owszem, jest to ważne, gdyż ma to być<br /> <br /> argument opcjonalny. W tym przypadku podstawowym problemem jest przekazywanie argumentów do funkcji, a zwłaszcza przekazywanie argumentów opcjonalnych. Jeśli pewna funkcja ma dwa argumenty, to w jej wywołaniu możesz przekazać oba, możesz także przekazać tylko pierwszy z nich bądź nie przekazywać żadnego. Nie możesz natomiast przekazać tylko drugiego argumentu.<br /> <br /> Rozdział 10.<br /> <br /> Nowy, piękny konstruktor Blog() obsługujący opcjonalne obrazki nie na wiele się przyda, jeśli metody prezentujące wpisy nie będą potrafiły wykorzystać nowych informacji dostępnych w obiekcie. Aby stworzyć wpisy obsługujące obrazki, musimy wykonać dwie czynności: 1<br /> <br /> Umieścić plik z obrazkiem w tym samym katalogu na serwerze WWW, w którym .............................. znajduje się strona Reni. .............................. ..................<br /> <br /> ..............................<br /> <br /> 01/0 .................. 9/20 08........................ ...... ...... ......<br /> <br /> ..............................<br /> <br /> ............<br /> <br /> .............................. Zdec .................. ydow ałam ...... się zam ...... ............ tę prze ......ówić ............ ......raża kostkę ......jącą 7×7 ............ ×7. Już ...... ...... ...... .................. zacz .................. ynam duc ...... how ......się o ...... ...... przy...... ...... goto...... ...... wyw...... ać na jej...... ukła dan ...... ie......................... ...... ...... .............................. .............................. .............................. .............................. 03/0 .................. 9/20 08........................ ...... ...... .............................. .............................. ...... Brał...... .................. am...... udz...... iał ...... w wyśc igu prze d ...... ...... loka...... ...... lnym...... ...... skle pem z ...... zaba...... ...... wka...... ...... mi,............ któr...... .................. y przes tał ............sprz ukła ......edaw ......ać ki....... ...... Mił ......dan ośni ...... cy ...... ukła...... ...... dan...... ...... ek — moc ...... ............ jest...... .................. z nam ......i! .............................. .............................. .............................. .............................. .............................. .............................. .............................. 05/0 .................. 9/20 08........................ ...... ...... .............................. .............................. ...... Dost...... .................. ałam zam ...... kost ......ówio kę...... ......ną 7×7...... ...... ×7...... ...... — ...... prze...... z jaki ś czas pew...... ...... nie będę ............ ............ milc .................. zeć. .............................. .............................. .............................. ............ .............................. .............................. .............................. .............................. 19/0 .................. 9/20 08........................ ...... ...... .............................. .............................. ...... O ran .................. y......... zaję ło mi ...... praw ...... ie...... ......to...... dwa...... tygo...... ...... dnie...... , ale...... w końc u uda ło...... ............ mi się ...... ............ ułoż...... .................. yć ...... tę kost kę!........................ ...... .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .............................. .........<br /> <br /> A zatem, jeśli chodzi o argumenty opcjonalne, to zastanawiając się nad nimi, musisz przede wszystkim uwzględnić możliwość pominięcia ich na liście argumentów wywołania. Oprócz tego warto byś zastanowił się także nad znaczeniem poszczególnych argumentów — te ważniejsze powinny się pojawić na początku listy. Mniej ważne argumenty, które mogą być uznane za opcjonalne, powinny być umieszczane na końcu listy argumentów wywołania. A ponieważ argument image konstruktora Blog() jest opcjonalny, zatem musi się pojawić na końcu listy argumentów, dzięki czemu bez problemów będziemy mogli go pominąć.<br /> <br /> 490<br /> <br /> Dodawanie obrazków do strony MagicznaKostka<br /> <br /> 2<br /> <br /> W kodzie tej strony utworzyć nowy wpis w blogu jako obiekt Blog.<br /> <br /> cube777.png<br /> <br /> Blog<br /> <br /> "O rany... zajęło mi to prawie dwa tygodnie, ale…" 19 września 2008<br /> <br /> Tworzenie własnych obiektów Po wykonaniu powyższych czynności uzyskujemy następujący kod, który utworzy nowy wpis, przy czym jako ostatni argument wywołania konstruktora Blog() przekazywany jest łańcuch znaków określający plik obrazka:<br /> <br /> new Blog(”O rany... zajęło mi to prawie dwa tygodnie, ale w końcu udało mi się ułożyć tę kostkę!”, new Date(”09/19/2008”), ”cube777.png”); Nazwa obrazka dołączonego do wpisu jest przekazywana jako trzeci argument wywołania konstruktora Blog().<br /> <br /> Obecnie każdy wpis w należy wyświetlać zgo blogu z logiką prezentowaną dnie poniższy pseudokod: przez<br /> <br /> Wyświetlanie obrazka Skoro udało się nam już utworzyć wpis zawierający obrazek, zostało już tylko jedno zadanie do dokończenia rozbudowy strony MagicznaKostka. Całe to gadanie o konstruktorach i argumentach opcjonalnych nie na wiele się przyda, jeśli kod wyświetlający wpisy nie będzie uwzględniał nowej właściwości image. Kod, o którym mowa, jest umieszczony w metodzie toHTML(). Wiemy już, że metoda ta jest odpowiedzialna za formatowanie wpisów w postaci kodu HTML, a teraz dodatkowo musi sprawdzać, czy właściwość image zawiera jakąś wartość, która może być nazwą pliku. W praktyce okazuje się, że każdy wpis będzie teraz można wyświetlić na dwa sposoby — z obrazkiem lub bez niego. Czynnikiem determinującym, który z tych sposobów prezentacji należy zastosować, jest podanie nazwy pliku obrazka.<br /> <br /> If (obrazek istnieje) Wyświetlamy wpis z obrazkiem Else Wyświetlamy wpis bez obrazka<br /> <br /> Zaostrz ołówek W metodzie toHTML() obiektu Blog brakuje kodu, który umożliwiałby wyświetlanie opcjonalnych obrazków. Uzupełnij brakujący fragment kodu i opisz jego działanie. if (.................) { blogHTML += "<strong>" + this.date.shortFormat() + "</strong><br /><table><tr><td><img src='" + this.image + "'/></td><td style='vertical-align:top'>" + this.body + "</td></tr></table><em>" + this.signature + "</em></p>"; } else { blogHTML += "<strong>" + this.date.shortFormat() + "</strong><br />" + this.body + "<br /><em>" + this.signature + "</em></p>"; }<br /> <br /> jesteś tutaj  491<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> W metodzie toHTML() obiektu Blog brakuje kodu, który umożliwiałby wyświetlanie opcjonalnych obrazków. Uzupełnij brakujący fragment kodu i opisz jego działanie.<br /> <br /> this.image if (.................) { blogHTML += "<strong>" + this.date.shortFormat() +<br /> <br /> image zostanie Jeśli we właściwości a, to warunek azk obr wa naz ona określ wartość true, instrukcji if przyjmie świetlony na stronie. wy ie tan zos a obrazek<br /> <br /> "</strong><br /><table><tr><td><img src='" + this.image + "'/></td><td style='vertical-align:top'>" + this.body + "</td></tr></table><em>" + this.signature + "</em></p>"; } else {<br /> <br /> W przeciwnym razie wpis zostanie wyświetlony w dotychczasowy sposób, czyli bez obrazka.<br /> <br /> blogHTML += "<strong>" + this.date.shortFormat() + "</strong><br />" + this.body + "<br /><em>" + this.signature + "</em></p>"; }<br /> <br /> Obiektowa strona blogu Renia jest wprost zachwycona. Dzięki obiektom prosta strona z jej blogiem została zdecydowanie rozbudowana i wyposażona w nowe możliwości, które na pewno będą się podobać odwiedzającym.<br /> <br /> . Wpis z obrazkiem<br /> <br /> 492<br /> <br /> Rozdział 10.<br /> <br /> Wiem… jest piękna, prawda?<br /> <br /> Tworzenie własnych obiektów<br /> <br /> Zaginarka stron Zegnij stronę wzdłuż pionowych linii, tak by oba mózgi się połączyły, a następnie rozwiąż zagadkę.<br /> <br /> Co większość skryptów może zyskać na zastosowaniu obiektów? To spotkanie umysłów!<br /> <br /> kto co<br /> <br /> podpis<br /> <br /> prezentuj()<br /> <br /> gdzie<br /> <br /> Brakujący obrazek<br /> <br /> dostarcz()<br /> <br /> kiedy<br /> <br /> Obiekty zapewniają skryptom tak wiele korzyści, że aż trudno określić, która z nich jest najważniejsza. Oczywiście, obiekty są bardzo różne, jednak niektóre z nich to klasa sama w sobie — stopniem złożoności znacznie przewyższają inne i mogą nieco komplikować sytuację, jednak w końcowym rozrachunku wynik jest oczywisty.<br /> <br /> jesteś tutaj  493<br /> <br /> 11. Zabijaj pluskwy — na śmierć!<br /> <br /> Dobre skrypty na złej drodze Z nimi nigdy nic nie wiadomo. Jednego dnia wszystko działa idealnie i wszyscy są szczęśliwi… a drugiego bam! Wszystko wokół wylatuje w powietrze. Wniosek — dobrze jest mieć pod ręką takiego gościa jak ja, który wszystko poskłada do kupy.<br /> <br /> Nawet najlepiej zaplanowany kod JavaScript może czasami zawieść. Kiedy tak się stanie, a kiedyś na pewno to nastąpi, to Twoim zadaniem będzie opanować nerwy i nie wpadać w panikę. Najlepsi programiści to nie tacy, którzy nigdy nie popełniają błędów — tacy są raczej kłamcami. Najlepsi programiści to tacy, którzy potrafią wytropić pluskwy i pozbyć się błędów, które sami popełnili. Co ważniejsze, najlepsi pogromcy pluskiew rozwijają dobre praktyki kodowania, które minimalizują niebezpieczeństwo popełniania najbardziej złośliwych i trudnych do wytropienia błędów. Z drugiej strony trochę prewencji nie zaszkodzi. Pamiętaj, że pluskwy pojawiają się zawsze, będziesz zatem potrzebował arsenału, by z nimi walczyć…<br /> <br /> to jest nowy rozdział  495<br /> <br /> Kurczę… pluskwa!<br /> <br /> Debugowanie w praktyce Oto szokujące fakty o życiu słodyczy — czekoladowy batonik może zawierać do 60 małych kawałków robaków. Niezależne od tego, jak przerażająca może się wydawać ta informacja, pamiętaj, że nie trzeba obawiać się pluskiew w kodzie JavaScript. Kod JavaScript możemy bowiem skontrolować znacznie dokładniej niż technologiczny proces produkcji czekoladowych batoników. Istnieje nawet specjalna grupa przeznaczona wyłącznie do wykrywania i usuwania błędów w JavaScripcie.<br /> <br /> TPP:<br /> <br /> Tropiciele Programistycznych Pluskiew Tropiciele Programistycznych Pluskiew, w skrócie TPP — tak właśnie mówi się o nich w środowisku. Olek właśnie niedawno dołączył do TPP jako tropiciel pluskiew w kodzie JavaScript i wprost nie może się doczekać, by sprawdzić swoje umiejętności i uwolnić sieć od paskudnego robactwa.<br /> <br /> Olek w TPP zajmuje się tropieniem pluskiew w kodzie JavaScript, wcześniej był wielbicielem czekoladowych batoników.<br /> <br /> Na drodze do sukcesu Olka stoi kilka problemów, które koniecznie wymagają jego uwagi. Zanim Olek stanie się sławnym i cenionym detektywem i pogromcą JavaScriptowych pluskiew, będzie musiał poznać arkana tajemnej sztuki testowania i uruchamiania skryptów.<br /> <br /> 496<br /> <br /> Rozdział 11.<br /> <br /> Baton czekoladowy z owadami? Fuj!<br /> <br /> Dla maniaków Zgodnie z przepisami sanitarnymi w USA w dowolnym batoniku czekoladowym może się znajdować do 60 „fragmentów” owadów. W odróżnieniu od tych szokujących realiów specjaliści z TPP prowadzą politykę „zerowej tolerancji” dla pluskiew w kodzie JavaScript. Weź z nich przykład!<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Przypadek wadliwego kalkulatora IQ Pierwszą sprawą, jaka pojawiła się na biurku Olka, jest skrypt kalkulatora IQ, który jest elementem strony obliczającej średni poziom IQ. Strona ta dokonuje obliczeń na podstawie wartości IQ zapisanych w tablicy, po czym tworzy grupy użytkowników o podobnych wartościach IQ. A zatem do skryptu przekazywana jest tablica liczb, a jego zadaniem jest wyznaczenie wartości średniej i wskazanie inteligencji przeciętnego użytkownika. Wszelkie pliki dotyczące tej sprawy znajdziesz na serwerze FTP wydawnictwa Helion, pod adresem ftp://ftp.helion.pl/przyklady/hfjsc.zip.<br /> <br /> 97<br /> <br /> 113<br /> <br /> Olek został poinformowany, że w tym skrypcie jest całkiem sporo błędów. Niestety, oprócz tego i rozbrajającego stwierdzenia, że „skrypt nie działa”, Olek nie dostał żadnych przydatnych wskazówek.<br /> <br /> 92, 105, 146, 77, var iqs = [ 113, 97, 86, 75, ]; 108 88, 64, 114, 165, 96, 97, Tablica z wartościami IQ przekazywana do skryptu.<br /> <br /> Sprawa nr 1 14 6<br /> <br /> Oto, jak skrypt powinien działać, niestety rzeczywistość jest inna. Średnia wartość IQ jest obliczana i przekształcana na „klasyfikację” inteligencji.<br /> <br /> Nie zawsze testowany i uruchamiany kod będzie dobrze napisany. jesteś tutaj  497<br /> <br /> Pokaż mi swoją przeglądarkę<br /> <br /> Wypróbuj kod w różnych przeglądarkach<br /> <br /> Internet Explorer<br /> <br /> Olek doszedł do wniosku, że wypróbowanie skryptu w kilku różnych przeglądarkach może mu pomóc w zdobyciu jakichś nowych informacji o problemie. Zaczął więc od Internet Explorera… Dwukrotne kliknięcie żółtego znak u wykrzyknika, umieszczonego w lewy rogu okna przeglądarki, powoduje m dolnym okienka z informacjami o błędach. wyświetlenie<br /> <br /> Internet Explorer wyświetlił komunikat o błędzie podczas wczytywania strony, jednak Olek nie jest pewny, czy tym informacjom można wierzyć. Rzut oka na kod pokazuje, że zmienna iqs istnieje, choć Internet Explorer twierdzi inaczej. Skoro okazało się, że przeglądarki nie zawsze precyzyjnie informują o zaistniałych problemach, Olek postanowił spróbować uruchomić kalkulator w przeglądarce Safari…<br /> <br /> Zmienna iqs jest zdefiniowana w kodzie, zatem komunikat wyś wiet w Internet Explorerze wydaje się lany być bezsensowny.<br /> <br /> Safari<br /> <br /> Jeśli policzysz zaczynając od wiersze kodu, przez Safari 1, to błąd zgłoszony w wierszu, ktbędzie zlokalizowany wydawał się óry początkowo w porządku.<br /> <br /> Safari informuje, że błąd jest zlokalizowany w całkowicie innym wierszu kodu, a co gorsza, Olkowi wydaje się, że ten wiersz nie powinien przysparzać żadnych problemów. W takim razie Olek postanowił spróbować zlokalizować błąd w Operze…<br /> <br /> Opera<br /> <br /> Konsole błędów dostępne w  przeglądarkach są wspaniałym narzędziem do diagnozowania problemów w  kodzie JavaScript.<br /> <br /> 498<br /> <br /> Rozdział 11.<br /> <br /> Numer wiersza jest inny, jednak kod wskazany przez Operę jest ten sam co w Safari.<br /> <br /> Zabijaj pluskwy — na śmierć! Trochę to dziwne. Opera wskazuje wiersz o innym numerze, jednak sam wiersz kodu jest najwidoczniej taki sam jak w Safari. To ostatnie jest dla Olka dobrą wiadomością. Jednak pomimo to Olek nie może znaleźć żadnego błędu we wskazanym kodzie. Dlatego zdecydował się spróbować w jeszcze innej przeglądarce — Firefoksie.<br /> <br /> ykle Firefox jest niezwchodzi użyteczny, jeśli czyny o określenie przy problemu.<br /> <br /> Firefox<br /> <br /> Firefox wskazał, że błąd występuje w wierszu 25., czyli tak samo jak przeglądarka Safari.<br /> <br /> <html> <head> <title>TPP. Sprawa nr 1: Kalkulator IQ

mózg
Gotowy do obliczenia średniego IQ.


umysł

Jaki błąd udało się znaleźć Olkowi i pomagającym mu przeglądarkom?

jesteś tutaj  499

To ptak, to samolot… nie, to Firefox

Firefox jest uznawany za przeglądarkę, która najlepiej nadaje się do diagnozowania i poprawiania błędów… przynajmniej na razie.

Firefox spieszy z pomocą Wziąwszy pod uwagę precyzję, z jaką Firefox opisał przyczynę błędu, Olek zdecydował się prowadzić dalsze badania przy użyciu tej przeglądarki. A zatem kliknął łącze wyświetlone w konsoli błędów Firefoksa, co spowodowało, że zostało wyświetlone okno z kodem źródłowym strony, a w nim zaznaczony wiersz poprzedzający ten, w którym pojawiły się problemy.

Dla maniaków

e Firefox nie tylko posiada doskonał nia rywa wyk do a ędzi wbudowane narz ze nie ysta korz na ala błędów, lecz także pozw bug, który specjalnego dodatku o nazwie Fire skryptów na ianie ham uruc i ie wan testo przenosi bezpłatnie pobrać go na całkowicie nowy poziom. Moż tfirebug.com/. w.ge //ww http: y i zainstalować ze stron

Kliknięcie tego łącza wyświetla okno z kodem strony; dodatkowo zostanie w nim zaznaczony wiersz kodu, w którym wystąpił błąd.

Ten kod jest przyczyną problemów.

Dzięki analizie komunikatu o błędzie wyświetlonego w Firefoksie Olek doszedł do wniosku, że numer wiersza, w którym występuje problem, podany przez Safari i Firefoksa (25) jest zgodny z prawdą. Można się o tym przekonać, klikając łącze w konsoli błędów Firefoksa i wyświetlając okno z kodem źródłowym, w którym problematyczny kod będzie wyróżniony. Jednak najważniejsze jest to, że Firefox bardzo trafnie i precyzyjnie opisał, co jest przyczyną problemów. Jak się okazało, problem był całkiem prosty, a jednocześnie z rodzaju tych, które bardzo łatwo przeoczyć.

500

Rozdział 11.

else if average < 50 {

Jak widać, w tej instrukcji if pominięto nawiasy, w których powinien być umieszczony waru nek.

Zabijaj pluskwy — na śmierć!

Nie ma

niemądrych pytań

P: Nie wiem, jak wyświetlić konsolę błędów w mojej przeglądarce. Jak mogę ją otworzyć?

O: Niestety każda przeglądarka jest inna, a w niektórych

odszukanie konsoli błędów JavaScriptu jest prawdziwym wyzwaniem. Aby zatem sprawdzić, jak można wyświetlić konsolę błędów w używanej przeglądarce, i korzystać z niej podczas testowania i uruchamiania skryptów, trzeba przejrzeć dokumentację przeglądarki. Na przykład w polskiej wersji przeglądarki Firefox należy w tym celu otworzyć menu Narzędzia, a następnie wybrać opcję Konsola błędów.

P: Co sprawia, że Firefox jest taki wyjątkowy? O: Twórcy tej przeglądarki wykonali wspaniałą robotę,

implementując jej mechanizmy raportowania błędów. Pod względem określania błędów występujących w skryptach oraz zapewniania pomocy w ich odnajdywaniu Firefox po prostu bije na głowę pozostałe przeglądarki. Nie oznacza to oczywiście, że te inne przeglądarki nie zostaną dopracowane i w przyszłości nie pobiją

Firefoksa pod względem łatwości wykrywania błędów. Jednak Firefox dowiódł, że jeśli chodzi o testowanie i uruchamianie stron zawierających kod JavaScript, jest niezwykle przydatną przeglądarką, zapewniającą ogromne możliwości.

P: O jakim błędzie informował Internet Explorer? O: Tak naprawdę to nikt nie ma stuprocentowej pewności. Wynika to z faktu, że błąd zgłoszony przez Internet Explorera jest związany z problemami z wczytaniem właściwości, które z kolei są efektem błędu napotkanego przez interpreter JavaScriptu. Wiemy, że kod nie jest prawidłowo wczytywany, gdyż interpreter uznał, iż zmienna iqs jest „niezdefiniowana”, co z kolei nie jest zgodne z prawdą, gdyż w kodzie można się przekonać, że zmienna ta jest tworzona. A zatem jedyną sytuacją, w jakiej może to być problemem, jest występowanie jakiegoś innego błędu, który uniemożliwia prawidłowe wczytanie skryptu. Stąd też kolejne pytania: „Czy w kodzie jest jeszcze jakiś inny błąd?” oraz „Co w zasadzie oznacza »niezdefiniowany« (ang. undefined)?”.

Proste sposoby usuwania błędów Olek jest bardzo podekscytowany tak szybkim znalezieniem błędu w skrypcie kalkulatora IQ. A wziąwszy pod uwagę, jak prostą poprawkę musi wprowadzić, Olek uznał, że świetnie sobie poradzi w tej robocie i błyskawicznie zdobędzie tytuł detektywa TPP.

To testowanie i uruchamianie jest łatwe. Dzięki niewielkiej pomocy Firefoksa moja praca będzie prosta, szybka i przyjemna.

else if (average < 50) { Umieszczenie warunku wewnątrz nawiasów rozwiązuje problem.

Dodanie brakujących nawiasów pozwala nam pozbyć się tej pluskwy raz na zawsze.

Czy to możliwe, by Olek po początkowym łatwym sukcesie zbytnio uwierzył we własne siły? Chyba powinien dokładnie sprawdzić działanie skryptu, zanim uzna, że sprawa jest załatwiona, i urwie się z roboty na resztę dnia…

jesteś tutaj  501

Co się tu stało?

Zgłoszony błąd nie zawsze jest przyczyną problemów Niestety Olek jeszcze nie skończył poprawiania skryptu kalkulatora IQ, ponieważ Firefox wciąż zgłasza problemy, choć tym razem uskarża się na coś zupełnie innego. Choć Olka kusi, by także i teraz zastosować tę samą taktykę i z założenia uwierzyć temu, co sugeruje przeglądarka, to jednak teraz Olek ma pewne wątpliwości co do prawidłowości zgłaszanego błędu.

Ten nawias klamrowy otwierający definicję tworzy parę z nawiasem, który według przeglądarki jest źródłem problemów, a zatem — jeśli chodzi o nawiasy klamrowe funkcji — wszystko jest w porządku.

TPP. Sprawa nr 1: Kalkulator IQ mózg
Gotowy do obliczenia średniego IQ.


Nie zawsze można ufać przeglądarce. To prawda, nawiasy klamrowe otaczające kod funkcji są w porządku. A zatem — choć czasami działa rewelacyjnie — to jednak tym razem Firefox najwyraźniej czepia się nie tego kodu, co trzeba. Warto jednak potraktować informację o nawiasach klamrowych jako ważną podpowiedź i dokładniej przyjrzeć się wszystkim nawiasom w kodzie.

502

Rozdział 11.

Zajmujemy się tylko jednym problemem naraz, więc ten komunikat ignorujemy.

Zabijaj pluskwy — na śmierć!

Bądź interpreterem JavaScriptu Twoim zadaniem jest wcielenie się w postać interpretera JavaScriptu i przemierzenie szlaku nawiasów klamrowych umieszczonych w kodzie w celu odszukania źródła problemów.

TPP. Sprawa nr 1: Kalkulator IQ



mózg
Gotowy do obliczenia średniego IQ.


jesteś tutaj  503

Rozwiązane ćwiczenia

Bądź interpreterem JavaScriptu Twoim zadaniem jest wcielenie się w postać interpretera JavaScriptu i przemierzenie szlaku nawiasów klamrowych umieszczonych w kodzie w celu odszukania źródła problemów.

TPP. Sprawa nr 1: Kalkulator IQ



mózg
Gotowy do obliczenia średniego IQ.


504

Rozdział 11.

błędem w JavaScripcie, którego jednak łatwo można uniknąć, zachowując odpowiednią uwagę i dbałość o szczegóły.

Zabijaj pluskwy — na śmierć!

efiniowane d z e i n Dzikie zmienne Olek nie może sobie zrobić nawet krótkiej przerwy, gdyż pluskwy w kodzie kalkulatora IQ pojawiają się jedna za drugą. Teraz Firefox twierdzi, że zmienna jest „niezdefiniowana”, co bardzo przypomina niewyjaśniony błąd, z którym Olek zetknął się na samym początku w Internet Explorerze. Jednak tym razem kłopotliwa niezdefiniowana zmienna nosi nazwę averag, a nie iqs.

Zwróć uwagę, że drugi błąd zniknął. Czasami poprawienie jednego błędu spowoduje rozwiązanie kilku problemów.

Nie mam pewności, jednak zawsze uważałem, że niezdefiniowana zmienna to taka, która nie została utworzona.

else if (averag < 81) { return "osobami, które powinny skorzystać z ćwiczeń umysłowych"; }

?

averag

niezdefiniowana?

Zaostrz ołówek Poniżej zapisz, co według Ciebie oznacza termin „niezdefiniowana” w kontekście ostatniego błędu wykrytego w trakcie poszukiwań Olka. ..................................................................................................................................................................................................... ..................................................................................................................................................................................................... .....................................................................................................................................................................................................

jesteś tutaj  505

Sprawdź odpowiedź

Zaostrz ołówek Rozwiązanie

Poniżej zapisz, co według Ciebie oznacza termin „niezdefiniowana” w kontekście ostatniego błędu wykrytego w trakcie poszukiwań Olka.

Termin „niezdefiniowana” (ang. undefined) odnosi się do zmiennej, która bądź to nie została utworzona

..................................................................................................................................................................................................... (przy użyciu słowa kluczowego var), bądź została utworzona, lecz jeszcze nie przypisano jej żadnej ..................................................................................................................................................................................................... wartości. W obu przypadkach występuje ten sam problem — zażądano odwołania do zmiennej, choć ..................................................................................................................................................................................................... zmienna ta nie ma jeszcze żadnej wartości. .....................................................................................................................................................................................................

Czasami błąd jest oczywisty

Coś tak trywialnego jak prosta literówka może wprowadzić w skrypcie istny chaos.

W naszym przypadku nie ma wątpliwości, że termin „niezdefiniowana” odnosi się do zmiennej, która została zastosowana, choć wcześniej jej nie utworzono. Nie ma też wątpliwości co do tego, że był to czysty przypadek. Problem został spowodowany przez prostą literówkę, przez co interpreter potraktował identyfikator jako zupełnie nową zmienną. function calcIQ Class(data) { // Obliczenie średniego IQ W przypadku prostych var average = 0; literówek, najtrudniejsze jest for (var i = 0; i < data.length; i++) { ich odnalezienie - poprawienie average += dat a[i]; } takich błędów nie nastręcza average = Math.f żadnych problemów. loor(average / data.length); // Zwracamy kla syfikację średni ego IQ if (average < 20) { return "osobami, które zabijają swoje telewizor } y"; else if (avera ge < 50) { return "osobami, które konieczni e powinny zajrze } ć do książek"; else if (avera ge < 70) { return "osobami, które powinny zajrzeć do ksi } ążek"; else if (avera g < 81) { return "osobami, które powinny skorzystać z ćwi } czeń umysłowyc h"; else if (avera ge < 91) { return "osobami uważanymi za tęp e"; } else if (avera ge < 111) { return "osobami przeciętnymi"; } else if (avera ge < 121) {

averag != average

506

Rozdział 11.

Poprawienie literówki rozwiązuje problem z niezdefiniowaną zm ienną.

else if (average < 81) { return "osobami, które powinny skorzystać z ćwiczeń umysłowych"; }

Zabijaj pluskwy — na śmierć!

Nie ma

niemądrych pytań

P: Czy jest jakaś różnica

pomiędzy komunikatami „undefined” a „not defined”?

O: Nie. Oba oznaczają dokładnie to samo. Po prostu różne przeglądarki używają jednego lub drugiego określenia. Pamiętaj, że określeń tych można używać zamiennie.

P: No dobrze, a zatem

kolejne pytanie: „Czy jest jakaś różnica pomiędzy zmienną niezdefiniowaną („undefined”) a wartością null?”.

O: Odpowiedź na to pytanie jest nieco

trudniejsza. Owszem, z czysto technicznego punktu widzenia istnieje różnica pomiędzy zmienną niezdefiniowaną i wartością null. W odróżnieniu od null „undefined” nie jest wartością, więc nie można nawet pomyśleć o próbie przypisania jej do zmiennej; to typ danych undefined, który zmienna automatycznie przyjmuje, jeśli jeszcze nie została jej przypisana żadna wartość. Z drugiej strony wartość null nigdy nie jest automatycznie przypisywana zmiennym. Czasami warto jednak przypisać właściwościom wartość null w ramach

inicjalizacji obiektu, aby było jasne, że obiekt nie został jeszcze utworzony. Zostawmy jednak te szczegóły techniczne. Najważniejsze jest bowiem to, że zarówno zmienna niezdefiniowana, jak i zmienna zawierająca null po umieszczeniu w kontekście logicznym (czyli na przykład w warunku instrukcji if) odpowiadają wartości false. Dlatego też często przed odwołaniem do zawartości obiektu można zobaczyć fragment kodu if (jakisObiekt), który sprawdza, czy obiekt w ogóle został utworzony.

JavaScript utworzy całkowicie nową zmienną. A ponieważ tej nowej zmiennej dopiero trzeba przypisać wartość, zatem próba porównania jej z inną wartością w instrukcji if stanowi poważny problem. Można by to porównać z próbą napisania recenzji filmu, jeszcze zanim zdecydowaliśmy, jaki film chcemy obejrzeć.

P: Żartujesz sobie ze mnie?

Pisząc teksty w edytorze, cały czas robię literówki, lecz nigdy nie było to przyczyną jakichkolwiek problemów. Dlaczego JavaScript jest taki wrażliwy?

P: Wciąż nie rozumiem, dlaczego O: Weź głęboki oddech i powtarzaj te prosta literówka sprawiła, że zmienna average stała się niezdefiniowana? Dlaczego tak się stało?

O: To fakt, że zmienna o nazwie average

została już wcześniej utworzona i zainicjowana, jednak JavaScript nie ma żadnej możliwości skojarzenia ze sobą zmiennych average i averag, choć ich nazwy są niemal identyczne. Z punktu widzenia JavaScriptu zmienna averag mogłaby równie dobrze nosić nazwę: srednia, shazbot lub lederhosen. W każdym z tych przypadków

cztery słowa tak długo, aż zapamiętasz je do końca życia: PRZYZWYCZAJ SIĘ DO TEGO! Nie piszemy programów, by czytali je ludzie, piszemy je dla komputerów, a te, niezależnie od używanego języka programowania, nie wybaczają nawet drobnych literówek. Nawet jeden znak w niewłaściwym miejscu może doprowadzić do awarii skryptu. Z pewną elastycznością traktowane są jedynie ogólnie pojęte odstępy otaczające kod JavaScript, w tym znaki odstępu oraz nowego wiersza. Jednak sam kod pisany w języku JavaScript musi być bardzo dokładny i precyzyjny.

Przetwarzając wartości IQ Po poprawieniu ostatniej literówki kalkulator IQ poprawiany przez Olka zaczął działać prawidłowo — wylicza średnią wartość IQ na podstawie danych zapisanych w tablicy, a następnie wyświetla wynik w formie tekstowej klasyfikacji. Olek może zatem zamknąć tę sprawę i cieszyć się z dobrze wykonanego zadania… Ale na jak długo?

Poprawienie literówki w nazwie zmiennej pozwoliło „ubić” kolejną pluskwę w kodzie.

Teraz, dzięki testom i poprawkom wprowadzonym przez Olka, kalkulator IQ działa zgodnie z oczekiwaniami.

jesteś tutaj  507

Chcesz wygrać — zadzwoń już teraz Kluczowe zagadnienia 

Choć większość przeglądarek udostępnia konsole błędów, które wyświetlają informacje o błędach napotykanych w skryptach JavaScript, to nie można na nich bezgranicznie polegać i ufać w ich poprawność.



Choć przeglądarki często prezentują ogólnikowe informacje o błędach, to zazwyczaj udostępniają jakieś podpowiedzi ułatwiające określenie miejsca, w którym należy szukać problemów.



Nawiasy klamrowe otaczające bloki kodu są częstym źródłem błędów w kodzie JavaScript — pamiętaj, by zawsze zapisywać pary nawiasów — otwierający i zamykający.



Literówki bardzo łatwo zrobić, lecz czasami trudno odszukać i poprawić — zawsze uważnie sprawdzaj umieszczane w kodzie nazwy identyfikatorów.

Przypadek błędów w połączeniach z radiem Olek niemal nie zdążył nacieszyć się swoim pierwszym sukcesem, kiedy na jego biurku pojawiła się kolejna sprawa. Ten nowy przypadek dotyczy skryptu obsługującego połączenia telefoniczne, w których rozmówcy podają odpowiedzi na ogłaszane w radio konkursy, i określającego zwycięzcę na podstawie numeru połączenia. Skrypt ma przechowywać liczbę odebranych połączeń i ogłaszać zwycięzcę dopiero wtedy, gdy liczba ta przekroczy pewną zadaną wartość, na przykład 7.

Przykro mi, jesteś rozmówcą numer 2.

Przykro mi, jesteś rozmówcą numer 5.

Sprawa nr

…sześć połączeń później…

508

Rozdział 11.

2

Zabijaj pluskwy — na śmierć!

Otwieranie dochodzenia Olek uznał, że przed otwarciem radiowej strony rejestracji rozmów telefonicznych warto zerknąć na jej kod źródłowy (możesz go znaleźć na serwerze FTP wydawnictwa Helion, pod adresem ftp://ftp.helion.pl/przyklady/hfjsc.zip) i przekonać się, jak została napisana. Olek ma nadzieję, że może od razu wpadnie na jakiś błyskotliwy pomysł, a może przynajmniej dowie się, jak skrypty na tej stronie powinny działać.

/title> ęzca konkursu telefonicznego< TPP. Sprawa nr 2: zwyci<br /> <br /> Początkowo licznik liczby rozmów jest ustawiany na zero.<br /> <br /> Inkrementacja liczby rozmów.<br /> <br /> Jeśli nie ma zwycięzcy, skrypt czyści pole służące do podania imienia rozmówcy.<br /> <br /> <script type="text/javascript"> // Sumaryczna liczba rozmów var callNum = 0; r, winningNum) { function checkWinner(form, calle w rozmó ę liczb jemy mentu Inkre // var callNum; ++callNum;<br /> <br /> numer Imię rozmówcy, owy zm ro j ce ają ięż zwyc za są lar mu for t iek oraz ob cji nk fu do ne wa przekazy checkWinner().<br /> <br /> Jeśli numer rejestrowanej rozmowy jest równy zwycięskiemu numerowi, to wyświetlany jest komunikat, a formularz zostaje przesłany na serwer.<br /> <br /> // Sprawdzamy zwycięzcę if (callNum = winningNum) { i dzisiejszym zwycięzcą!"); wcą numer + callNum + "... alert(caller + ", jesteś rozmó form.submit(); } else { ia rozmówcy // Czyścimy pole do podania imien mentById('caller'); var callerField = document.getEle rozmówca"; jsce callerField.value = "Następny a focus() ustawia mie Metod anym us(); d.foc callerFiel wprowadzania we wskaz callerField.select(); mencie strony.<br /> <br /> ele<br /> <br /> } <br /> <br /> } </script> </head><br /> <br /> Metoda select() zaznacza wartość zapisaną w polu tekstowym.<br /> <br /> To nazwa skryptu działającego na serwerze, który zapisuje dane zwycięzcy.<br /> <br /> <body> diocall.php" method="POST"> <form name="callform" action="ra " /> radio alt=" <img src="radio.png" " name="caller" type="text" /> Imię rozmówcy: <input id="caller onię" ="Dzw value on" "butt type= t <inpu r').value, 7)" /> , document.getElementById('calle onclick="checkwinner(this.form W momencie naciśnięcia przycisku </form> Dzwonię wywoływana jest metoda </body> checkWinner(). </html><br /> <br /> Zaostrz ołówek Pomóż Olkowi rozpocząć pracę nad tą sprawą i wskaż, ile według Ciebie jest błędów w kodzie skryptu rejestrującego rozmowy.<br /> <br /> Żadnego<br /> <br /> Jeden<br /> <br /> Dwa<br /> <br /> Pięć<br /> <br /> Trzy Cztery<br /> <br /> jesteś tutaj  509<br /> <br /> Słucham? Błędy składniowe<br /> <br /> Zaostrz ołówek Rozwiązanie Żadnego<br /> <br /> Jeden<br /> <br /> Pomóż Olkowi rozpocząć pracę nad tą sprawą i wskaż, ile według Ciebie jest błędów w kodzie skryptu rejestrującego rozmowy.<br /> <br /> Dwa<br /> <br /> Pięć<br /> <br /> Trzy Cztery<br /> <br /> Popchnijmy sprawę do przodu i pomóżmy Olkowi znaleźć te wszystkie błędy.<br /> <br /> Problem weryfikacji błędów składniowych (pluskwa nr 1) Skoro poznaliśmy już podstawowy sposób działania kodu, możemy uruchomić Firefoksa i sprawdzić, co się dzieje, kiedy spróbujemy wyświetlić w nim stronę rejestrującą połączenia. Podobnie do innych błędów, z którymi już się spotkaliśmy, Firefox natychmiast zgłosi wykrycie błędu składniowego (ang. syntax error), czyli błędu wynikającego z niezgodności kodu z zasadami języka JavaScript.<br /> <br /> Ten błąd wskazuje na linię zawierającą konkatenację łańcuchów znaków.<br /> <br /> Wykrycie błędu składniowego zawsze powoduje wyświetlenie przez przeglądarkę jakiegoś komunikatu. To zapewnia nam bardzo ważny i wygodny punkt startowy dla poszukiwań błędu.<br /> <br /> 510<br /> <br /> Rozdział 11.<br /> <br /> O błędach składniowych zawsze informuje przeglądarka, zakładając, że opcja raportowania błędów jest włączona.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Uwaga na te łańcuchy znaków Firefox wskazał wiersz kodu zawierający konkatenację łańcuchów znaków, co stanowi podpowiedź, by wiersz ten bardzo dokładnie przeanalizować. W wierszu tym znajduje się wywołanie funkcji alert(), wewnątrz którego łączymy kilka łańcuchów znaków i zmienne caller oraz callNum. if (callNum == winningNum) alert(caller +<br /> <br /> + callNum +<br /> <br /> );<br /> <br /> Te dwa literały łańcuchowe są łączone z dwiema zmiennymi.<br /> <br /> Czy cudzysłowy i apostrofy nie powinny zawsze występować w parach? To chyba ważne, prawda?<br /> <br /> Stosowanie par cudzysłowów ma kluczowe znaczenie dla kodu JavaScript Cudzysłowy i apostrofy zawsze muszą występować w parach, w przeciwnym razie interpreter JavaScriptu nie jest w stanie określić, gdzie jeden łańcuch się kończy, a drugi zaczyna. W przypadku strony rejestrującej rozmowy telefoniczne brakuje cudzysłowu zamykającego jeden z łączonych literałów łańcuchowych. Bez wątpienia jest to błąd składniowy, gdyż wprowadza on w błąd interpreter, który nie jest w stanie określić, gdzie kończy się łańcuch znaków. Aby poprawić ten błąd, należy dodać brakujący cudzysłów na końcu łańcucha znaków:<br /> <br /> if (callNum = winningNum) { alert(caller + ", jesteś rozmówcą numer<br /> <br /> + callNum + "... i dzisiejszym zwycięzcą!");<br /> <br /> if (callNum = winningNum) { alert(caller + ", jesteś rozmówcą numer " + callNum + "... i dzisiejszym zwycięzcą!");<br /> <br /> Dodanie cudzysłowu definitywnie kończy żywot tej pluskwy.<br /> <br /> jesteś tutaj <br /> <br /> 511<br /> <br /> Konsekwencja ma znaczenie<br /> <br /> Cudzysłowy, apostrofy i konsekwencja Pomijanie cudzysłowów lub apostrofów w łańcuchach znaków to jedynie połowa problemu. Ponieważ w JavaScripcie i HTML-u można używać obu tych znaków do zapisywania łańcuchów znaków (w przypadku JavaScriptu) lub atrybutów (w HTML-u), zatem podczas jednoczesnego stosowania cudzysłowów i apostrofów należy zachować szczególną uwagę.<br /> <br /> W cudzysłowach są zapisywane wszystkie atrybuty HTML.<br /> <br /> <input type="button" value="Dzwonię" onclick="checkwinner(this.form, document.getElementById('caller').value, 7)" /><br /> <br /> Powyższa technika — polegająca na zapisywaniu atrybutów HTML w cudzysłowach oraz łańcuchów znaków w kodzie JavaScript umieszczanym w atrybutach HTML — jest doskonałym rozwiązaniem. Nic jednak nie stoi na przeszkodzie, by zamienić oba znaki (cudzysłów z apostrofem i na odwrót), co pokazuje poniższy przykład:<br /> <br /> <input type='button' value='Dzwonię'<br /> <br /> Apostrofy służą do zapisywania łańcuchów znaków w kodzie JavaScript umieszczanym w atrybutach HTML. Nowoczesny standard XHTML nie pozwala, by wartości atrybutów były zapisywane w apostrofach.<br /> <br /> onclick='checkwinner(this.form, document.getElementById("caller".value, 7)' /> Obecnie wartości atrybutów są zapisywane w apostrofach, natomiast cudzysłowy są zarezerwowane dla łańcuchów znaków w kodzie JavaScript.<br /> <br /> Założeniem tego rozwiązania jest, by cudzysłowy były używane w kodzie jednego typu, a apostrofy — w kodzie innego typu. A ponieważ nowe wersje języków HTML i XHTML wymagają zapisywania atrybutów w cudzysłowach, zatem warto trzymać się tych ustaleń i zapisywać wartości atrybutów w cudzysłowach, a łańcuchy znaków w kodzie JavaScript — w apostrofach. Problem powstaje natomiast w sytuacji, gdy wewnątrz łańcucha musimy umieścić cudzysłów lub apostrof, choć znak ten został już przez nas użyty jako określenie końców łańcucha.<br /> <br /> Podczas zapisywania atrybutów HTML zawierających łańcuchy znaków JavaScript konieczne jest naprzemienne stosowanie cudzysłowów<br /> <br /> alter('Precz z McDonald's!');<br /> <br /> i apostrofów.<br /> <br /> Czy taki kod zadziała?<br /> <br /> WYTĘŻ umysł<br /> <br /> Co się stanie, kiedy będziemy musieli użyć cudzysłowu lub apostrofu w łańcuchu znaków, który sam jest zapisany pomiędzy tymi znakami?<br /> <br /> 512<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Kiedy apostrof nie jest apostrofem, użyj odwrotnego ukośnika Bardzo często spotykany błąd jest związany z umieszczeniem w łańcuchu znaków cudzysłowu lub apostrofu, który zostaje błędnie zinterpretowany jako granica łańcucha. A zatem komunikat o błędzie, jaki właśnie zobaczyliśmy, dotyczy błędu składniowego, gdyż interpreter JavaScriptu nie wie, które apostrofy wyznaczają granicę łańcucha, a które powinny być potraktowane jako prawdziwe apostrofy. Na szczęście istnieje prosty sposób zaznaczenia, że znak ma być potraktowany jak „zwyczajny” znak. Sposobem tym jest tak zwany znak unikowy, czyli odwrotny ukośnik (\) umieszczony przed znakiem, który należy potraktować dosłownie. alert('Precz z McDonald\'s!');<br /> <br /> Jak widać, znak apostrofu został poprzedzony odwrotnym ukośnikiem, a zatem JavaScript nie będzie mieć żadnych wątpliwości, że naprawdę chcemy umieścić apostrof w łańcuchu znaków, a nie chodzi nam o zakończenie tego łańcucha. Oczywiście, podobny efekt moglibyśmy osiągnąć, zmieniając znaki, w jakich jest zapisany łańcuch, na cudzysłowy. alert("Precz z McDonald's!");<br /> <br /> Tu już nie trzeba stosować znaku unikowego.<br /> <br /> Takie rozwiązanie działa świetnie, ale co zrobić, gdy będziemy mieli do czynienia z następującym łańcuchem: alert("Krzyczeli: "Precz z McDonald's!"");<br /> <br /> Znaki unikowe są stosowane, by wybrane znaki w łańcuchu były odczytywane dosłownie.<br /> <br /> Tutaj nie musimy umieszczać znaku unikowego, choć zastosowanie go jest dobrym pomysłem.<br /> <br /> Powyższy łańcuch zawiera zarówno cudzysłowy, jak i apostrof, które należy potraktować dosłownie. W takim przypadku zastosowanie odwrotnego ukośnika jest jedynym wyjściem z sytuacji. Co więcej, kiedy będziemy mieć do czynienia z takimi łańcuchami, to najlepszym rozwiązaniem będzie umieszczanie odwrotnego ukośnika przed wszystkimi znakami, które chcemy potraktować dosłownie, nawet jeśli nie jest to konieczne (w powyższym przykładzie nie musimy umieszczać odwrotnego ukośnika przed apostrofem): alert("Krzyczeli: \"Precz z McDonald\'s!\"");<br /> <br /> ćwiczenie<br /> <br /> Popraw błędy związane z nieprawidłowym użyciem cudzysłowów i apostrofów w poniższych fragmentach kodu; staraj się używać znaku unikowego: var wiadomosc = 'Kocham McDonald's!'; ..................................................................................... var odpowiedz = "Powiedziała: "Kocham McDonald's!""; ..................................................................................... <input type="button" value="Zwycięzca" onclik="wreczNagrode("Reni");" /> .....................................................................................<br /> <br /> jesteś tutaj  513<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> ćwiczenie Rozwiązanie Ten apostrof nie musi być poprzedzany znakiem unikowym, gdyż cały łańcuch znaków jest zapisany w cudzysłowach.<br /> <br /> Popraw błędy związane z nieprawidłowym użyciem cudzysłowów i apostrofów w poniższych fragmentach kodu; staraj się używać znaku unikowego: var wiadomosc = 'Kocham McDonald's!'; var wiadomosc = 'Kocham McDonald\'s!'; ..................................................................................... var odpowiedz = "Powiedziała: "Kocham McDonald's!""; var odpowiedz = "Powiedziała: \"Kocham McDonald\'s!\""; ..................................................................................... <input type="button" value="Zwycięzca" onclik="wreczNagrode("Reni");" /> <input type="button" value="Zwycięzca" onclik="wreczNagrode('Reni');" /> ..................................................................................... W tym przypadku znaki unikowe nie będą działać, gdyż mamy do czynienia z łańcuchem znaków JavaScript zapisanym w atrybucie HTML. Rozwiązaniem problemu jest zastąpienie wewnętrznych cudzysłowów apostrofami.<br /> <br /> Nie tylko zmienne mogą być niezdefiniowane (pluskwa nr 2) Udało się nam zatem poprawić pierwszy błąd, Olek jednak doskonale wie, że jego zadanie się na tym nie skończyło. Teraz skrypt rejestrujący odpowiedzi uruchamia się już dobrze — bez zgłaszania jakichkolwiek błędów. Jednak wystarczy kliknąć przycisk Dzwonię, aby ujawnił się kolejny problem. Tym razem wydaje się, że problem ma coś wspólnego z funkcją checkWinner().<br /> <br /> Kliknięcie przycisku Dzw onię powoduje pojawienie się błędu, który w jakiś spo jest związany z funkcj sób ą checkWinner().<br /> <br /> Z jakiegoś powodu ta funkcja jest niezdefiniowana.<br /> <br /> 514<br /> <br /> Rozdział 11.<br /> <br /> Wyświetlony numer wiersza nic nam nie pomoże, gdyż doskonale wiemy, że pierwszy wiersz kodu . HTML na pewno jest w porządku<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Standardowi podejrzani: lista Posiadając już pewne doświadczenia w testowaniu skryptów, Olek postanowił przejrzeć niedawno napisaną listę najczęściej popełnianych błędów pojawiających się w kodzie JavaScript. Może błąd, z którym mamy do czynienia, okaże się podobny do jednego z błędów, które już kiedyś napotkaliśmy?<br /> <br /> * Nawiasy — pominięte lub bez pary.<br /> <br /> ....................................... ....................................... ....................................... .......................................<br /> <br /> * Nawiasy klamrowe — pominięte lub bez pary.<br /> <br /> ....................................... ........................................ ........................................<br /> <br /> * Literówki — błędne nazwy identyfikatorów.<br /> <br /> ........................................ ........................................ ........................................<br /> <br /> * Błędne użycie apostrofów lub cudzysłowów.<br /> <br /> r, winningNum) { function checkWinner(form, calle w // Inkrementujemy liczbę rozmó um; var callN ++callNum;<br /> <br /> Nazwa funkcji checkWinner() pojawia się tylko w dwóch miejscach kodu.<br /> <br /> ......................................... ......................................... ......................................... .........................................<br /> <br /> // Sprawdzamy zwycięzcę if (callNum = winningNum) { dzisiejszym zwycięzcą!"); wcą numer " + callNum + "... i alert(caller + ", jesteś rozmó form.submit(); } Hm… else { wcy rozmó ia imien ia podan do pole // Czyścimy mentById('caller'); var callerField = document.getEle rozmówca"; callerField.value = "Następny callerField.focus(); callerField.select(); } } </script> </head><br /> <br /> Ściągawka Olka z najczęściej popełnianymi błędami.<br /> <br /> <body> diocall.php" method="POST"> <form name="callform" action="ra " /> radio alt=" .png" <img src="radio " name="caller" type="text" /> Imię rozmówcy: <input id="caller onię" <input type="button" value="Dzw r').value, 7)" /> , document.getElementById('calle onclick="checkwinner(this.form </form> </body> </html><br /> <br /> Zaostrz ołówek Pomóż Olkowi i zakreśl rodzaj błędu, który według Ciebie napotkaliśmy w skrypcie rejestrującym rozmowy. Brakujący lub niedopasowany nawias klamrowy. Literówka w nazwie identyfikatora.<br /> <br /> Brakujący lub niedopasowany nawias. Błędnie zastosowany cudzysłów lub apostrof.<br /> <br /> Jakiś zupełnie inny błąd.<br /> <br /> jesteś tutaj  515<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Pomóż Olkowi i zakreśl rodzaj błędu, który według Ciebie napotkaliśmy w skrypcie rejestrującym rozmowy.<br /> <br /> Brakujący lub niedopasowany nawias klamrowy. Literówka w nazwie identyfikatora.<br /> <br /> Brakujący lub niedopasowany nawias. Błędnie zastosowany cudzysłów lub apostrof.<br /> <br /> Jakiś zupełnie inny błąd. ie przypadkowo W wywołaniu funkcji w jej nazw kwinner(). wpisano małą literkę „w” — chec pt uznał, iż Ta literówka sprawiła, że JavaScri a nie została któr chodzi o zupełnie inną funkcję, ana. iniow zdef Cała poprawka sprowadza się do zmiany literki „w” na „W” w wywołaniu checkWinner().<br /> <br /> Poprawa literówki w nazwie funkcji zlikwidowała pluskw ę numer 2.<br /> <br /> <input type="button" value="Dzwonię" onclick="checkWinner(this.form, document.getElementById('caller').value, 7)" /><br /> <br /> Każdy jest zwycięzcą (pluskwa nr 3) Choć udało się nam pozbyć tej paskudnej „niezdefiniowanej” pluskwy, w skrypcie rejestrującym rozmowy telefoniczne wciąż pojawiają się błędy. Na szczęście przeglądarka nie zgłasza już żadnych problemów. Niestety okazuje się, że każda osoba, która zadzwoni, jest zwycięzcą. Co gorsza, skrypt przypisuje wszystkim zwycięski numer, choć w rzeczywistości faktyczny numer rozmowy jest zupełnie inny. Jeśli Olek nie znajdzie szybko rozwiązania tego problemu, będzie to oznaczało konieczność rozdania wielu nagród… oj, wielu! Najdziwniejsze jest to, rozmowy jest prezentowże numer zwycięski, choć w rze any jako numer czy rejestrowana rozmowa wistości ma całkowicie inny numer.<br /> <br /> Każdy dzwoniący jest uznawany za zwycięzcę.<br /> <br /> 516<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Testowanie przy użyciu okienka dialogowego Wiemy, że sprawdzenie zwycięskiego numeru odbywa się poprzez porównanie zmiennej callNum z argumentem winningNum funkcji checkWinner(). Jednak ten kod wydaje się być w porządku. Jak widać, musimy nieco dokładniej sprawdzić, co się dzieje ze zmienną callNum. Na pierwszy rzut okna wydaje się, że ten kod jest w porządku.<br /> <br /> ... if (callNum = winningNum) { ...<br /> <br /> Warto zatem spróbować prześled zić zmiany wartości zmiennej callNum zachodzące w tym kodzie.<br /> <br /> Czy można w jakiś sposób sprawdzać wartość zmiennej w różnych miejscach skryptu?<br /> <br /> Do podglądania wartości zmiennej doskonale nadaje się informacyjne okienko dialogowe.<br /> <br /> Okienka dialogowe mogą służyć jako okienka do sprawdzania wartości zmiennych. Jak się okazuje, okienka dialogowe mogą służyć także do innych celów niż wyświetlanie informacji użytkownikom strony. Z powodzeniem można je także stosować podczas pisania i uruchamiania kodu JavaScript jako okienka prezentujące wartości zmiennych. Co więcej, można ich także używać, by sprawdzać, czy wybrane fragmenty kodu są wywoływane zgodnie z naszymi oczekiwaniami. W naszym przypadku musimy skorzystać z okienka dialogowego, by sprawdzić, co się dzieje ze zmienną callNum.<br /> <br /> 7<br /> <br /> Okienko dialogowe pozwala wyświetlić wartość zmiennej, w tym przypadku jest to zmienna callNum.<br /> <br /> callNum<br /> <br /> jesteś tutaj  517<br /> <br /> Alarmy też są narzędziem<br /> <br /> Obserwowanie zmiennych przy użyciu okienek dialogowych Czujka (ang. watch) to termin związany z technikami testowania i uruchamiania programów, określający ciągłą obserwację zmiennej w trakcie działania programu. Informacyjne okienka dialogowe zapewniają uproszczoną wersję czujek, które nie pozwalają na ciągłą obserwację zmiennych, lecz i tak są całkiem użyteczne. Okienko takie można wyświetlać w dowolnym miejscu kodu, gdzie tylko zachodzą jakieś wątpliwości co do wartości przyjmowanych przez analizowaną Coś nie jest w porządku, zmienną.<br /> <br /> alert(callNum); if (callNum = winningNum) { alert(caller + ", jesteś rozmówcą numer form.submit(); }<br /> <br /> zmienna callNum powinna mieć wartość odpowiadającą numerowi odbieranego połączenia.<br /> <br /> + callNum + "... i dzisiejszym zwycięzcą!");<br /> <br /> Olek uzmysłowił sobie, że skrypt z jakiegoś powodu traktuje zmienne callNum oraz winningNum za równe, choć tuż przed porównaniem wartość zmiennej callNum nie jest liczbą (NaN). Choć to, że zmienna callNum ma wartość NaN, jest dziwne i niepokojące, to jednak Olek zdecydował się przesunąć wywołanie funkcji alert() do kodu wewnątrz instrukcji if, by sprawdzić, czy tam coś się zmienia. if (callNum = winningNum) { alert(callNum); alert(caller + ", jesteś rozmówcą numer form.submit(); }<br /> <br /> + callNum + "... i dzisiejszym zwycięzcą!");<br /> <br /> A teraz, bezpośrednio po sprawdzeniu warunku w instrukcji if, okazuje się, że zmienna callNum ma wartość 7.<br /> <br /> WYTĘŻ umysł<br /> <br /> Skoro okienko dialogowe wykazało, że w jakiś cudowny sposób w jednym wierszu kodu zmienna callNum przyjmuje wartość 7, powiedz, co według Ciebie może być przyczyną tych problemów. Co przyszło Olkowi do głowy?<br /> <br /> 518<br /> <br /> Rozdział 11.<br /> <br /> Bingo! Chyba wiem, o co chodzi.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Testowanie z użyciem okienka dialogowego Temat dzisiejszego wywiadu:<br /> <br /> Okienko dialogowe dzieli się swoją awersją do pluskiew Head First: Muszę przyznać, że w doniesieniach o tobie<br /> <br /> jest wiele sprzeczności. Niektórzy twierdzą, że denerwujesz wiele osób. A z drugiej strony słyszałem pogłoski, że jesteś najlepszym przyjacielem programistów testujących i uruchamiających skrypty. Czy możesz coś na ten temat powiedzieć?<br /> <br /> Okienko: Ci pierwsi ludzie to jacyś wariaci. Przecież jestem wspaniałe. Jestem także całkiem proste — przekazujesz mi jakieś informacje do wyświetlenia i bum!… wyskakuję i prezentuję, co chciałeś. I to wszystko. Co w tym złego?<br /> <br /> Head First: Hm… myślę, że chodzi o to „wyskakiwanie”. Ostatnio wszystko, co „wyskakuje”, ma złą reputację… głównie przez te koszmarne reklamy, które czasami pojawiają się po prostu wszędzie.<br /> <br /> Okienko: Och… no tak. Domyślam się, że one mogą<br /> <br /> naprawdę uprzykrzyć człowiekowi życie. Ale przecież nie możesz winić młotka za to, że jakiś kiepski stolarz nie potrafi się nim posługiwać. Rozumiesz, o co mi chodzi?<br /> <br /> Head First: Chcesz przez to powiedzieć, że te wszystkie<br /> <br /> niesympatyczne rzeczy, jakie o tobie słyszałem, wynikają stąd, że jesteś nieprawidłowo stosowane?<br /> <br /> Okienko: Otóż to. Jak powiedziałem, ja robię tylko to, co mi każą. Jeśli każesz mi pojawiać się co pięć sekund i prezentować całą masę głupkowatych reklam, to tak zrobię. Nie twierdzę, że mi się to podoba, jednak nie mam żadnego wyboru. Ale prawdę mówiąc, sądziłem, że chcesz się zapytać o mój wkład w testowanie skryptów. Head First: A tak, przepraszam. Zatem słyszałem wiele dobrego o tym, jak pomagasz programistom wykrywać i poprawiać błędy w kodzie JavaScript. Jak to robisz?<br /> <br /> Okienko: To naprawdę proste. Powiedzmy, że w skrypcie<br /> <br /> jest jakaś zbuntowana zmienna, która nie wiadomo dlaczego przyjmuje zupełnie bezsensowne wartości. Programista jest wkurzony na maksa, napojony kofeiną po dziurki od nosa — wiesz, co chcę powiedzieć — i desperacko poszukuje jakiegoś sposobu, by wyświetlać wartości zmiennej w różnych miejscach skryptu. I co robi? Prosi mnie o wyświetlenie tej zmiennej.<br /> <br /> Okienko: Wcale nie. Wystarczy wywołać mnie wiele razy; za każdym razem w innym miejscu skryptu.<br /> <br /> Head First: Rozumiem. A czy kiedykolwiek miałeś problemy związane z tym, że używano cię jako narzędzie do testowania skryptów? Okienko: Cóż, muszę przyznać, że nie jestem idealnym rozwiązaniem, w przypadkach gdy jestem wywoływane w kodzie wykonywanym wiele razy, na przykład w pętli. Head First: A dlaczego? Okienko: Cóż… pamiętaj, że ja… wyskakuję, a żebym<br /> <br /> zniknęło, trzeba mnie kliknąć. Jeśli będę wyskakiwać wiele razy, to także wiele razy trzeba mnie kliknąć.<br /> <br /> Head First: Coś w tym jest. Słyszałem także, że jesteś<br /> <br /> przydatne nawet wtedy, gdy problemy nie są związane z danymi.<br /> <br /> Okienko: O tak. Jest wiele sytuacji, gdy nie wiadomo<br /> <br /> dokładnie, kiedy jakiś fragment kodu jest wywoływany albo czy w ogóle to nastąpi. Krótkie odwołanie do mnie wewnątrz takiego kodu pozwala dowiedzieć się, czy kod został wywołany. W takich przypadkach staję się pewnego rodzaju alarmem informującym, czy kod został wywołany, czy nie.<br /> <br /> Head First: Czy uważasz, że pomimo tych wszystkich zastosowań jesteś jedynie narzędziem zastępczym?<br /> <br /> Okienko: Oczywiście! I wcale nie mam tego nikomu za złe. Nie twierdzę, że nie podobałoby mi się, gdybym to robił na stałe; ale wiesz… traktuję to jako takie dodatkowe zajęcie dla dobra ogółu.<br /> <br /> Head First: Dobra, dziękuję bardzo za twój czas<br /> <br /> i wyjaśnienie nam swojej roli w testowaniu skryptów. Mam nadzieję, że jeszcze kiedyś się pojawisz.<br /> <br /> Head First: Ale niby jak możesz pokazać zmiany wartości zmiennej w różnych miejscach skryptu? To chyba trudne?<br /> <br /> jesteś tutaj  519<br /> <br /> Czy to logiczne?<br /> <br /> Zła logika może być przyczyną błędów Olek doszedł do wniosku, że przyczyną aktualnych problemów musi być błąd logiczny — czyli kod, który z punktu widzenia JavaScriptu jest całkowicie poprawny, a jednocześnie błędnie realizuje zamiary programisty. W naszym przypadku zamiast operatora porównania == został użyty znak =, co oznacza, że zamiast porównania wartości zmiennych callNum i winningNum wartość winningNum zostanie zapisana w zmiennej callNum. Subtelne? Owszem. A jak poważnych problemów potrafi przysporzyć. ... if (callNum =<br /> <br /> Okazało się, że ko „wyglądał” na pr d, który aw zawierał subteln idłowy, y do wykrycia błąd. i trudny<br /> <br /> winningNum) {<br /> <br /> ... ... if (callNum == winningNum) { ...<br /> <br /> Prawdziwy problem z błędami tego typu polega na tym, że nie przeszkadzają one przeglądarce i nie powodują zgłoszenia jakichkolwiek komunikatów, co odróżnia je na przykład od błędów składniowych. W naszym przypadku interpreter JavaScriptu nie skarży się, gdyż operator przypisania „zwraca” wartość — jest to wartość zmiennej winningNum, która w warunku instrukcji if zostaje automatycznie zamieniona na logiczną wartość true (gdyż jest różna od zera). Innymi słowy, kod jest najzupełniej poprawny, choć wcale nie działa tak, jak byśmy sobie tego życzyli.<br /> <br /> Dodanie drugiego znaku = unicestwiło pluskwę numer 3.<br /> <br /> A zatem komunikaty o błędach logicznych nigdy nie są wyświetlane w konsoli błędów przeglądarki? Błędy logiczne latają za nisko, by radar mógł je wykryć. Wykrywanie błędów logicznych jest tak trudne, ponieważ zazwyczaj nie objawiają się on tak jak błędy składniowe. Choć błędy skryptów wyświetlane w przeglądarce mogą nas nieco przerażać, to jednak w rzeczywistości są one dla nas prawdziwym dobrodziejstwem, gdyż — jakby na to nie spojrzeć — są to błędy, które już udało się wykryć, a niejednokrotnie także i zlokalizować. Natomiast błędy logiczne nie naruszają żadnych reguł JavaScriptu, dlatego bardzo rzadko przeglądarkom udaje się je wykryć. Z drugiej strony czasami zdarza się, że błędy logiczne w kodzie powodują, iż podczas działania strony będą się pojawiać zwyczajne błędy JavaScriptu. Jeśli na przykład przez jakiś błąd logiczny pewna zmienna nie zostanie zainicjowana, to doprowadzi to do wystąpienia błędu związanego z próbą dostępu do niezdefiniowanej zmiennej, co z kolei wygeneruje odpowiedni komunikat o błędzie, zawierający nazwę zmiennej. Dlatego też czasami błędy logiczne mogą zaoszczędzić nam wiele czasu i wysiłku, jaki musielibyśmy poświęcić na poszukiwania błędów.<br /> <br /> 520<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć! Kluczowe zagadnienia  <br /> <br />  <br /> <br /> Błędy składniowe występują w kodzie, który narusza reguły JavaScriptu, i zazwyczaj są wychwytywane przez interpreter tego języka. Na początku i końcu łańcucha znaków należy umieszczać te same znaki — apostrofy lub cudzysłowy.<br /> <br />  <br /> <br />  <br /> <br /> W atrybutach HTML związanych z obsługą zdarzeń należy naprzemiennie stosować cudzysłowy i apostrofy (oczywiście w parach). Informacyjne okienka dialogowe stanowią bardzo prosty, lecz użyteczny sposób obserwowania zmiennych w trakcie działania skryptu.<br /> <br />  <br /> <br /> Bardzo często zdarza się, że w warunku sprawdzającym równość dwóch zmiennych zamiast operatora porównania == zostanie zastosowany operator przypisania =.<br /> <br /> Nie ma<br /> <br /> niemądrych pytań<br /> <br /> P: Czy znaki unikowe są<br /> <br /> stosowane wyłącznie przed cudzysłowami lub apostrofami?<br /> <br /> O: Nie, JavaScript obsługuje kilka znaków<br /> <br /> specjalnych, które można poprzedzać znakiem unikowym. Na przykład można zastosować znak \t, by umieścić w łańcuchu znak tabulacji. Na podobnej zasadzie znak nowego wiersza jest reprezentowany przez znak \n. Natomiast chcąc umieścić w łańcuchu znak odwrotnego ukośnika, należy użyć sekwencji \\. Jednym z miejsc, w którym znaki unikowe mogą być stosowane efektywnie, jest informacyjne okienko dialogowe. Można w nich z powodzeniem stosować znak \t, by wyrównywać tekst przy użyciu znaków tabulacji, oraz \n, by określić jego rozmieszczenie przy użyciu znaków nowego wiersza.<br /> <br /> P: O co chodzi z ograniczeniem możliwości stosowania znaków unikowych w atrybutach HTML?<br /> <br /> O: Ograniczenie to wynika z prostego<br /> <br /> faktu, że atrybuty HTML nie podlegają regułom języka JavaScript; a przynajmniej nie, jeśli chodzi o znaki, pomiędzy jakimi mają być zapisywane wartości atrybutów. A zatem choć znaki w łańcuchu znaków należącym do kodu JavaScript, który jest umieszczony w atrybucie HTML, można poprzedzać znakiem unikowym, to jednak nie mogą to być te same znaki, pomiędzy którymi została zapisana wartość atrybutu. Jeśli wciąż nie do końca rozumiesz, o co w tym wszystkim chodzi, to wyobraź to<br /> <br /> sobie w następujący sposób. HTML traktuje atrybut jako zwyczajną wartość, która musi być zapisana pomiędzy identycznymi znakami — cudzysłowami lub apostrofami. I tyle. A zatem niezależnie od tego, który z tych znaków wybierzesz i umieścisz na początku atrybutu, HTML przyjmie, że odnalezienie drugiego identycznego znaku zakończy wartość atrybutu. Dzieje się tak dlatego, że HTML nie przetwarza zawartości atrybutu i nie poszukuje w niej znaków unikowych JavaScriptu. Natomiast znaki unikowe działają wewnątrz atrybutów HTML, przy czym nie mogą kolidować ze znakami wyznaczającymi zawartość atrybutu. Jest tak dlatego, że wartość atrybutu w końcu zostanie zinterpretowana jako kod JavaScript; oczywiście zakładając, że mówimy tu o atrybutach, w których są określane procedury obsługi zdarzeń.<br /> <br /> P: Czy nie ma bardziej<br /> <br /> wyszukanych narzędzi do testowania i uruchamiania, zapewniających dokładną kontrolę nad procesem testowania?<br /> <br /> O: Owszem, jest kilka takich narzędzi.<br /> <br /> I powiem więcej — byłoby całkiem dobrze, gdybyś wpadł na pomysł, żeby się im dokładniej przyjrzeć i wypróbować przynajmniej jedno z nich. Powinieneś jednak zrozumieć, że wyrobienie w sobie dobrych praktyk programistycznych w połączeniu z technikami testowania przedstawionymi w tym rozdziale bardzo Ci pomoże w pisaniu skryptów pozbawionych błędów.<br /> <br /> P: Co dokładnie się dzieje, kiedy<br /> <br /> kod JavaScript próbuje odwołać się do niezdefiniowanej wartości lub funkcji?<br /> <br /> O: Pamiętaj, że zmienna jest<br /> <br /> niezdefiniowana, jeśli nie została wcześniej utworzona bądź nigdy nie przypisano jej żadnej wartości. W obu tych przypadkach wartość właściwości nie jest znana, a konkretnie rzecz biorąc, to właśnie ta wartość nie jest zdefiniowana. Dlatego też próba odczytania tej wartości i wykorzystania jej do wykonania jakiejś operacji nie ma najmniejszego sensu. To właśnie dlatego JavaScript generuje błąd. Podobna sytuacja zachodzi, gdy wywołujemy jakąś funkcję, lecz interpreter JavaScriptu nie jest w stanie znaleźć jej na podstawie podanej nazwy. Funkcja jest niezdefiniowana, co oznacza, że wywoływanie jej nie ma sensu, bo nie ma czego wywoływać. Także w tym przypadku JavaScript uznaje to za błąd, gdyż nie ma sposobu, by sensownie wykonać wskazany kod.<br /> <br /> P: A dlaczego przed warunkiem<br /> <br /> if na stronie rejestrującej zgłoszenia telefoniczne zmienna callNum jest wyświetlana jako NaN?<br /> <br /> O: Tego jeszcze nie wiemy. Choć stanowi<br /> <br /> to dla nas podpowiedź, że z kodem skryptu wciąż jest coś nie w porządku. Dlatego też eksterminacja pluskiew musi trwać nadal…<br /> <br /> jesteś tutaj  521<br /> <br /> Błąd składniowy kontra błąd logiczny<br /> <br /> Pogawędki przy kominku<br /> <br /> Temat dzisiejszej pogawędki: Błąd składniowy oraz Błąd logiczny rozprawiają o swej miłości do skryptów niskiej jakości.<br /> <br /> Błąd składniowy:<br /> <br /> Błąd logiczny:<br /> <br /> Hej, słyszałem o tobie. Słyszałem, że się doskonale ukrywasz. Mnie jednak interesuje to, czy lubisz kiepskie skrypty równie mocno jak ja? No pewnie. Nie ma nic lepszego niż skrypt, który z pozoru wydaje się być dobrze napisany, lecz w rzeczywistości w niewidoczny dla nikogo sposób wywołuje masę problemów. Nie zgadzam się z tobą. Ja lubię skrypty, które przysparzają problemów od samego początku i to w widoczny sposób. To mnie kręci. Wystarczy, że umieścisz mnie w kilku miejscach skryptu, a ja już się postaram, by przeglądarka skręciła się z bólu. A niby co w tym jest takiego fajnego? Każdy wie, że podstępny atak jest znacznie bardziej skuteczny. Rozumiesz… uśpij ich czujność, udając, że wszystko jest w porządku, a potem powolutku zacznij ujawniać niewielkie problemy tu i tam. Jeśli zrobisz to dostatecznie dobrze, to może nawet zaczną się zastanawiać, czy aby ich przeglądarka dobrze działa. Doceniam twoje pokręcone podejście do sprawy, ale problem polega na tym, że w ogóle pozwalasz, by skrypt działał. A to mi się nie podoba. Ja lubię skutecznie zabijać skrypty. I tu masz rację. Boleję nad tym, ale w odróżnieniu od ciebie nie udało mi się jeszcze opracować dobrej, skutecznej metody ogłaszania swojej obecności. Albo jeszcze lepiej — doprowadzenia do awarii całej przeglądarki w jednym pięknym wybuchu. To by było super. Ech, byłoby, było… szkoda tylko, że jesteśmy tacy ograniczeni, jeśli chodzi o to, jak wiele szkód możemy wyrządzić. Jasne, że fajnie jest popsuć stronę i uniemożliwić jej prawidłowe działanie, ale wkurza mnie to, że nie mamy dostępu do niczego więcej. Chłopie, ależ by była zabawa, gdybym mógł się dobrać do dysku twardego pełnego ważnych danych.<br /> <br /> 522<br /> <br /> Rozdział 11.<br /> <br /> Faktycznie, to byłoby niesamowite. Jesteś pewny, że nie ma jakiegoś sposobu, żebyśmy się tam dostali?<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Błąd składniowy<br /> <br /> Błąd logiczny<br /> <br /> Nie… interpreter JavaScriptu przyblokował nas na dobre. No trudno… Ale i tak jest masa zabawy z tym, co możemy popsuć. Czy mówiłem ci o mojej małej sztuczce ze znakami równości: = i ==? Nie. Jak to działa? Fantastycznie! Programista chce napisać ==, by porównać ze sobą dwie wartości, ale przez pomyłkę wpisuje =. W efekcie zamiast porównania wychodzi mu przypisanie. To przekomiczna sprawa, bo potem całe godziny denerwuje się, nie mogąc znaleźć przyczyny problemu. A najlepsze jest to, że interpreter JavaScriptu wcale nie jest od niego mądrzejszy, bo z technicznego punktu widzenia kod jest całkowicie poprawny. Jak ci się to udaje? Wiesz… mam masę sposobów. Super jest to, że cały czas działam zgodnie z prawem, a pomimo to udaje mi się przysparzać tyle problemów. Ja też mam nieco podobną sztuczkę. Polega na tym, że programiści zapominają zakończyć wiersza kodu średnikiem. Sztuczka jest super, ponieważ interpreter przymyka na to oko i traktuje kod tak, jakby poszczególne instrukcje były w osobnych wierszach. Ale w końcu jakiś nadgorliwy programista spróbuje „zoptymalizować” kod i połączy obie instrukcje w jedną — a wtedy wyskakuję i pokazuję, do czego jestem zdolny. Ta sztuczka nigdy się nie starzeje — zawsze mam przy niej doskonały ubaw. To przypomniało mi o jeszcze jednej sztuczce, którą mogę się pochwalić. Uwielbiam, kiedy programiści decydują się zmieniać argumenty funkcji po jej napisaniu. To działa bez pudła — na sto procent zapomną o zmodyfikowaniu wszystkich wywołań, a przecież wszystkie muszą zostać zmienione i dostosowane do nowych argumentów. Jeśli wszystko pójdzie dobrze, to interpreter niczego nie zauważy, do funkcji zostaną przekazane niewłaściwe dane, a funkcja zwróci nieoczekiwane wyniki. To mi się podoba, bo gdyby interpreter zauważył zmianę, to na pewno zadziałałby i ostrzegł programistę, wyświetlając komunikat o błędzie. Wiesz co… Zaczynam uważać, że powinniśmy stworzyć zespół — myślę, że w ten sposób moglibyśmy wyrządzić więcej szkód i problemów. Świetny pomysł. To co? Zaczynamy? Jasne, do dzieła…<br /> <br /> jesteś tutaj  523<br /> <br /> Naprawdę zapluskwione radio<br /> <br /> Nikt nie wygrywa! (pluskwa nr 4) Olek zaczyna dochodzić do wniosku, że całe to testowanie i poprawianie błędów wcale nie jest takie łatwe, jak mu się początkowo wydawało. Po poprawieniu błędu logicznego w warunku instrukcji if okazało się, że skrypt w ogóle nie wyznaczał zwycięzcy konkursu. A zatem sytuacja zmieniła się diametralnie — wcześniej wszyscy wygrywali, a teraz nie wygrywa nikt. Jeśli Olek czegoś szybko nie wymyśli, to ten błąd na pewno nadwątli samoocenę niektórych osób. Rozmówca nr 3.<br /> <br /> Rozmówca nr 1.<br /> <br /> Wydaje się, że teraz zamiast samych zwycięzców mamy samych przegranych — skrypt nigdy nie wyznacza zwycięzcy.<br /> <br /> To naprawdę trudny do wykrycia błąd. Chyba użyję okienek dialogowych, by sprawdzić, co się dzieje ze zmienną przechowującą numer rozmowy.<br /> <br /> Rozmówca nr 7.<br /> <br /> Ten rozmówca powinien zostać zwycięzcą konkursu.<br /> <br /> 524<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Przytłoczony ilością denerwujących okienek dialogowych Olek próbuje zastosować okienka dialogowe, by móc obserwować zmienną callNum i na tej podstawie spróbować określić, co się dzieje. Problem w tym, że dotarcie do siódmego rozmówcy wymaga zamknięcia wielu okienek dialogowych, a to jest męczące. Olek próbował wyświetlać okienka dialogowe w kilku różnych miejscach kodu, jednak jest przytłoczony dużą ilością całkowicie nieprzydatnych dialogów i nie ma pomysłu, jak zacząć…<br /> <br /> Odbieranie kolejnych rozmów powoduje, że zmienna callNum przyjmuje coraz to inne, dziwne wartości.<br /> <br /> Wadą zastosowania okienek dialogowych do podglądania wartości zmiennych jest to, że stają się one męczące, jeśli zostaną użyte w kodzie, który jest wielokrotnie wykonyw any, na przykład w pętli.<br /> <br /> Czyż nie byłoby cudownie, gdyby istniał jakiś sposób obserwacji zmiennych bez konieczności wyświetlania okienek dialogowych?<br /> <br /> jesteś tutaj  525<br /> <br /> Czy nie do tego służy konsola?<br /> <br /> Pomocna może się okazać konsola testowa przeglądarki Większość przeglądarek udostępnia konsolę testową, w rzeczywistości konsolę błędów, która odpowiada za wyświetlanie informacji o błędach pojawiających się w skryptach. Konsole te są niezwykle przydatne, gdy trzeba sprawdzić, czy skrypt działa dobrze, a w wielu przypadkach pomagają także zdiagnozować przyczynę problemów. Na szczególną uwagę zasługuje konsola błędów przeglądarki Firefox. Konsole przeglądarek są doskonałym źródłem informacji na temat problemów występujących w skryptach, a w szczególności na temat błędów składniowych.<br /> <br /> Już wcześniej, podczas szukania problemów, często korzystaliśmy z konsoli błędów Firefoksa.<br /> <br /> Konsole błędów są super i w ogóle… ale jak mogą mi pomóc w obserwowaniu zmiennych?<br /> <br /> Jeśli chodzi o obserwację zmiennych, to konsole w ogóle nam nie pomagają. Choć konsole błędów przeglądarek są użyteczne, to jednak w żaden sposób nie pomagają nam w obserwowaniu wartości zmiennych. Ale sytuacja nie jest aż tak straszna — okazuje się bowiem, że stworzenie własnej konsoli pozwalającej na obserwację zmiennych wcale nie jest nierealne.<br /> <br /> 526<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Tworzymy własną konsolę do testowania skryptów Pomysł samodzielnego pisania konsoli do testowania skryptów może się z początku wydawać onieśmielający, jednak w rzeczywistości okazuje się, że jej działanie polega jedynie na wyświetlaniu tekstu wtedy, gdy o to poprosimy. Kluczowe znaczenie ma to, że konsola musi wyświetlać informacje bezpośrednio na stronie, a nie w okienku dialogowym. Osobne okienko dialogowe mogłoby być używane, gdyby nie zmuszało użytkownika do ciągłego klikania przycisku OK, jednak wyświetlanie informacji testowych bezpośrednio na stronie jest prostsze i równie efektywne.<br /> <br /> Informacje są wyświetlane tuż poniżej głównej zawartości stro ny, w specjalnym obszarze testowym .<br /> <br /> Każdy wiersz jest osobnym komunikatem testowym.<br /> <br /> Zaostrz ołówek Wyobraź sobie konsolę do testowana skryptów, która pozwoli Olkowi wyświetlać komunikaty w formie listy prezentowanej w specjalnym, dynamicznie utworzonym obszarze strony. Naszkicuj, jakie komponenty byłyby według Ciebie potrzebne do stworzenia takiej konsoli, i określ, jak będą ze sobą współpracować. Nie zapomnij uwzględnić niestandardowego obiektu JavaScriptu, który będzie obsługiwał konsolę.<br /> <br /> jesteś tutaj  527<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie Konsola testowa została zaprojektowana jako obiekt o nazwie DebugConsole, który ma jedną właściwość i jedną metodę.<br /> <br /> Wyobraź sobie konsolę do testowana skryptów, która pozwoli Olkowi wyświetlać komunikaty w formie listy prezentowanej w specjalnym, dynamicznie utworzonym obszarze strony. Naszkicuj, jakie komponenty byłyby według Ciebie potrzebne do stworzenia takiej konsoli, i określ, jak będą ze sobą współpracować. Nie zapomnij uwzględnić niestandardowego obiektu JavaScriptu, który będzie obsługiwał konsolę.<br /> <br /> le<br /> <br /> DebugConso<br /> <br /> shaded<br /> <br /> Każde wywołanie metody displayMsg() powoduje wyświetlenie nowego wiersza z komunikatem testowym w obszarze strony przeznaczonym na konsolę.<br /> <br /> displayMsg()<br /> <br /> Sama konsola jest tworzona na stronie jako element div.<br /> <br /> Właściwość shaded jest wartością logiczną, która może przyjmować jedynie wartości true lub false. Jej wartości zmieniają się naprzemiennie dla każdego kolejnego komunikatu, określając tło, na jakim jest on wyświetlany.<br /> <br /> div Elementy HTML określające obszar konsoli na stronie są tworzone dynamicznie przez obiekt DebugConsole, co oznacza, że użytkownik chcąc skorzystać z konsoli, nie będzie musiał umieszczać na stronie żadnych dodatkowych elementów.<br /> <br /> 528<br /> <br /> Rozdział 11.<br /> <br /> Wszystkie komunikaty są wyświetlane we własnych elementach div, które na stronie są umieszczane wewnątrz elementu div definiującego obszar całej konsoli.<br /> <br /> ...<br /> <br /> div<br /> <br /> div<br /> <br /> div<br /> <br /> "callNum: 1"<br /> <br /> "callNum: 2"<br /> <br /> "callNum: 3"<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Magnesiki z JavaScriptem — Rozwiązanie W kodzie konsoli brakuje jeszcze kilku fragmentów. Uzupełnij puste miejsca, by dokończyć kod obiektu DebugConsole.<br /> <br /> function DebugConsole() { // Tworzymy obszar konsoli na stronie );<br /> <br /> (<br /> <br /> var consoleElem = document. consoleElem.id = "debug"; consoleElem.style.fontFamily = "monospace"; consoleElem.style.color = "#333333"; document.body.<br /> <br /> (consoleElem);<br /> <br /> consoleElem.<br /> <br /> (document.<br /> <br /> ("hr"));<br /> <br /> // Właściwość określająca kolor tła komunikatu = false;<br /> <br /> this. }<br /> <br /> DebugConsole.prototype.displayMsg = function(msg) { // Tworzymy komunikat var msgElement = document.createElement("div"); (msg));<br /> <br /> msgElement.appendChild(document.<br /> <br /> : "#FFFFFF"; msgElement.style.backgroundColor = this.shaded ? "#EEEEEE" ); var consoleElem = document.getElementById( );<br /> <br /> consoleElem.appendChild(<br /> <br /> atu // Zmiana wartości właściwości określającej kolor tła komunik this.shaded =<br /> <br /> this.shaded;<br /> <br /> }<br /> <br /> msgElement<br /> <br /> "div" shaded<br /> <br /> appendChild<br /> <br /> "debug"<br /> <br /> createElement<br /> <br /> ! createTextNode<br /> <br /> jesteś tutaj  529<br /> <br /> Rozwiązanie magnetycznego ćwiczenia<br /> <br /> Magnesiki z JavaScriptem — Rozwiązanie W kodzie konsoli brakuje jeszcze kilku fragmentów. Uzupełnij puste miejsca, by dokończyć kod obiektu DebugConsole.<br /> <br /> function DebugConsole() { // Tworzymy obszar konsoli na stronie<br /> <br /> consoleElem.id = "debug";<br /> <br /> consoleElem.style.color = "#333333";<br /> <br /> consoleElem.<br /> <br /> appendChild appendChild<br /> <br /> (consoleElem); (document.<br /> <br /> createElement<br /> <br /> // Właściwość określająca kolor tła komunikatu this.<br /> <br /> shaded<br /> <br /> );<br /> <br /> Element div konsoli jest dopisywany do dokumentu, co oznacza, że zostanie on dodany na samym końcu strony.<br /> <br /> consoleElem.style.fontFamily = "monospace";<br /> <br /> document.body.<br /> <br /> "div"<br /> <br /> ..... ( ....... eElement creat.......<br /> <br /> var consoleElem = document.<br /> <br /> = false;<br /> <br /> Początkowo właściwość ta ma wartość false, co oznacza, że pierwszy wpis zostanie wyświetlony na białym tle. DebugConsole.prototype.displayMsg = function(msg) {<br /> <br /> }<br /> <br /> Pierwszym elementem potomnym umieszczanym wewnątrz konsoli jest pozioma kreska, która ma oddzielić komunikaty prezentowane w konsoli od pozostałej zawartości strony. Komunikat jest dodawany do konsoli jako potomny element div.<br /> <br /> // Tworzymy komunikat var msgElement = document.createElement("div"); msgElement.appendChild(document.<br /> <br /> createTextNode<br /> <br /> ("hr"));<br /> <br /> (msg));<br /> <br /> : "#FFFFFF"; msgElement.style.backgroundColor = this.shaded ? "#EEEEEE" var consoleElem = document.getElementById( "debug" consoleElem.appendChild(<br /> <br /> msgElement );<br /> <br /> );<br /> <br /> Kolor tła zmienia się wraz z każdym komunikatem, dzięki czemu atu komunik tła kolor // Zmiana wartości właściwości określającej poszczególne komunikaty można this.shaded; this.shaded = ! łatwiej odczytać.<br /> <br /> }<br /> <br /> 530<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Testujemy konsolę Olek wprost nie może się doczekać, kiedy po raz pierwszy zastosuje konsolę, by sprawdzić, co jest nie tak ze skryptem rejestrującym rozmowy telefoniczne. Dlatego też zaimportował plik debug.js na stronie rejestrującej telefony do radia, a następnie utworzył obiekt DebugConsole w sekcji nagłówka. Ten kod, umieszczony w sekcji nagłówka strony, tworzy obiekt konsoli i zapisuje go w zmiennej globalnej.<br /> <br /> <script type="text/javascript"> // Zmienna globalna konsoli var console = new DebugConsole(); ...<br /> <br /> Niestety, coś poszło niezgodnie z planem. Kiedy Olek spróbował uruchomić konsolę, okazało się, że jedynie pogorszył swoją sytuację, gdyż do istniejących błędów doszły nowe, związane z samą konsolą.<br /> <br /> Nie rozumiem, jak to możliwe, że obiekt document.body nie ma żadnych właściwości. Przecież to w nim jest umieszczana cała zawartość strony.<br /> <br /> Przeglądarka twierdzi, że obiekt document.body nie ma żadnych właściwości.<br /> <br /> Wydaje się, że do skryptu zakradła się całkowicie nowa pluskwa… ohyda!<br /> <br /> Wiersz kodu, w którym pojawia się nowy błąd, próbuje jedynie dodać nowy węzeł potomny (div) do drzewa dokumentu, co przecież nie powinno przysparzać najmniejszych problemów. document.body.appendChild(console);<br /> <br /> Chodzi o coś innego, choć najwidoczniej ma to jakiś związek z obiektem DebugConsole.<br /> <br /> WYTĘŻ<br /> <br /> umysł<br /> <br /> Jak sądzisz, czy to wina powyższego fragmentu kodu, że nagle okazuje się, iż obiekt document.body jest pusty?<br /> <br /> jesteś tutaj  531<br /> <br /> Obserwując upływ czasu<br /> <br /> Czekając na stronę Problem z konsolą musi być związany z czasem, a konkretnie z rozmieszczeniem w czasie momentu wczytywania strony i prób uzyskania programowego dostępu do jej zawartości.<br /> <br /> Elementy HTML tworzące treść strony są wczytywane wraz z treścią strony, a zatem po wczytaniu sekcji nagłówka.<br /> <br /> Sekcja nagłówka strony WWW jest wczytywana przed jej zawartością, a zatem w tym momencie nie jest w jeszcze dostępny żaden z elementó zawartości strony.<br /> <br /> <html> <head> <title>TPP. Sprawa nr 2: zwycięzca konkursu telefonicznego</ti tle> <script type="text/javascript" src="debug.js"></script> <script type="text/javascript"> // Zmienna globalna konsoli błędów var console = new DebugConsole(); // Sumaryczna liczba rozmów var callNum = 0;<br /> <br /> Rozumiem… zatem kod skryptu, który jest wykonywany w sekcji nagłówka, nie ma dostępu do elementów HTML umieszczonych w treści strony.<br /> <br /> function checkWinner(form, caller, winningNum) { // Inkrementujemy liczbę rozmów var callNum; ++callNum; // Sprawdzamy zwycięzcę if (callNum == winningNum) { alert(caller + ", jesteś rozmówcą numer " + callNum + "... i dzisiejszym zwycięzcą!"); form.submit(); } else { // Czyścimy pole do podania imienia rozmówcy var callerField = document.getElementById('caller'); callerField.value = "Następny rozmówca"; callerField.focus(); callerField.select(); }<br /> <br /> } </script> </head><br /> <br /> <body> <form name="callform" action="radiocall.php" method="POST"> <img src="radio.png" alt="radio" /> Imię rozmówcy: <input id="caller" name="caller" type="tex t" /> <input type="button" value="Dzwonię" onclick="checkWinner(this.form, document.getElementById('ca ller').value, 7)" /> </form> </body> </html><br /> <br /> Kod JavaScript wykonywany w sekcji nagłówka nie ma dostępu do treści strony. Ponieważ sekcja nagłówka jest wczytywana przed samą treścią strony, dlatego należy uważać, by w kodzie JavaScript, który jest wykonywany bezpośrednio podczas wyczytywania strony, nie odwoływać się do elementów HTML umieszczonych w treści strony. Może się wydawać, że ograniczenie to jest dosyć dziwne, kiedy jednak weźmiemy pod uwagę, że kod zazwyczaj nie jest wykonywany podczas przetwarzania sekcji nagłówka, to okaże się, że ma to sens.<br /> <br /> 532<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> A co z funkcjami, które są definiowane w sekcji nagłówka strony? Czy także one nie mają dostępu do treści strony?<br /> <br /> W momencie wczytywania strony nie jest wykonywany cały kod, który jest umieszczony w sekcji nagłówka. Umieszczenie kodu w funkcji, która jest zapisana w sekcji nagłówka strony, to nie to samo co wykonanie kodu w momencie wczytywania strony. Kod funkcji nie jest wykonywany aż do momentu, gdy funkcja zostanie wywołana. Jednak cały kod umieszczony poza funkcjami jest wykonywany natychmiast, gdy przeglądarka go wczyta. To właśnie ten kod może przysparzać problemów. W przypadku obiektu DebugConsole okazuje się, że nie można go tworzyć bezpośrednio w sekcji nagłówka, gdyż jego wykonanie wymaga dostępności treści strony.<br /> <br /> Zaostrz ołówek Zapisz, kiedy i gdzie według Ciebie należy tworzyć obiekt DebugConsole, by mieć pewność, że jego konstruktor może bezpiecznie uzyskać dostęp do zawartości strony. ........................................................................................................................................................................... ........................................................................................................................................................................... ........................................................................................................................................................................... ........................................................................................................................................................................... ...........................................................................................................................................................................<br /> <br /> jesteś tutaj  533<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Zapisz, kiedy i gdzie według Ciebie należy tworzyć obiekt DebugConsole, by mieć pewność, że jego konstruktor może bezpiecznie uzyskać dostęp do zawartości strony.<br /> <br /> Przeglądarka informuje nas o zakończeniu wczytywania strony, zgłaszając zdarzenie onload. ........................................................................................................................................................................... A zatem obiekt DebugConsole powinien być tworzony w odpowiedzi na zgłoszenie tego ........................................................................................................................................................................... zdarzenia. Niemniej sama zmienna konsoli, console, powinna być tworzona jak pokazano ........................................................................................................................................................................... wcześniej, czyli w sekcji nagłówka. Po prostu jedynie nie wywołujemy konstruktora, ........................................................................................................................................................................... by utworzyć obiekt dopiero w odpowiedzi na zdarzenie onload. ...........................................................................................................................................................................<br /> <br /> du Przeniesienie ko kt ie ob tworzącego widuje lik le so DebugCon ną pluskwę związa ą. ol ns z ko<br /> <br /> <body onload="console = new DebugConsole();"> towej Teraz obiekt konsoli tesiedzi na jest tworzony w odpow zdarzenie onload.<br /> <br /> Błędy najgorsze ze wszystkich: błędy czasu wykonywania programu Nasz ostatni problem związany z pustym obiektem document.body był przykładem błędu czasu wykonania programu. Błędy tego typu zazwyczaj pojawiają się tylko w określonych sytuacjach, najczęściej właśnie podczas działania skryptu. Czasami błędy czasu wykonywania występują wyłącznie w bardzo szczególnych okolicznościach, na przykład w razie podania przez użytkownika szczególnej kombinacji danych wejściowych lub wykonania konkretnej liczby iteracji pętli. Niejednokrotnie zdarza się, że są to najtrudniejsze błędy do wykrycia, co głównie wynika z faktu, że ich występowanie jest trudne do przewidzenia. Czasami poważnym wyzwaniem jest samo powtórne doprowadzenie do wystąpienia takiego błędu, kiedy już raz udało się go zauważyć.<br /> <br /> Błędy czasu wykonania są spowodowane zajściem specyficznych warunków podczas działania skryptu.<br /> <br /> Błąd w naszej konsoli testowej był typowym błędem czasu wykonania, spowodowanym przez próbę pobrania danych, które jeszcze nie zostały wczytane — taki problem może wystąpić wyłącznie podczas działania skryptu.<br /> <br /> 534<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Bestiariusz JavaScriptu Błędy czasu wykonywania wraz z pozostałymi dwoma rodzajami błędów, które poznaliśmy wcześniej, tworzą trójcę najbardziej uciążliwych pluskiew w JavaScripcie; tworzą ją błędy składniowe, logiczne oraz błędy czasu wykonywania skryptu. Błędy każdego z tych trzech rodzajów mogą w widoczny sposób pojawiać się w skrypcie i to niejednokrotnie nawet w tym samym czasie. Poznanie i zrozumienie różnic pomiędzy nimi jest ważnym czynnikiem, mającym duży wpływ na to, czy będziemy w stanie skutecznie je odnajdywać i zwalczać.<br /> <br /> Błąd czasu wykonywania<br /> <br /> Błąd składniowy<br /> <br /> czas Pojawia się wyłącznie wtedy, gdy pod e pewne nion speł aną zost wykonywania skryptu kownik użyt gdy kład przy na określone warunki, e, dan ne pew nie stro na wpisze w formularzu gdy bądź , użyć obsł ie stan w których skrypt nie jest ten m zani ktu, obie do się ołać skrypt spróbuje odw any. icjow zain lub zostanie utworzony<br /> <br /> Ten błąd jest spowodowa ny niezgodnością z zasadami języka JavaSc ript, co oznacza, że kod nie nadaje się do wykonania przez interpreter JavaScriptu.<br /> <br /> <html> <head> /title> ęzca konkursu telefonicznego< <title>TPP. Sprawa nr 2: zwyci t" src="debug.js"></script> <script type="text/javascrip<br /> <br /> Błąd logiczny<br /> <br /> Ten błąd jest spowodowany przez niewłaściwą logikę działania skryptu, często jest to kod, który ma zrealizować konkretną operację, lecz przypadkowo został napisany w taki sposób, że robi coś zupełnie innego. Zdarza się, że kod zawierający takie błędy działa dokładnie tak, jak zamierzono, przez co programista może mieć problemy z określeniem , od czego powinien zacząć testowanie.<br /> <br /> t"> <script type="text/javascrip błędów // Zmienna globalna konsoli le(); Conso Debug new var console = w // Sumaryczna liczba rozmó var callNum = 0; caller, winningNum) { function checkWinner(form, rozmów ę liczb jemy mentu Inkre // var callNum; ++callNum; console.displayMsg("callNum:<br /> <br /> " + callNum);<br /> <br /> // Sprawdzamy zwycięzcę { if (callNum = winningNum) wcą numer alert(caller + ", jesteś rozmó form.submit(); } else {<br /> <br /> m zwycięzcą!"); + callNum + "... i dzisiejszy<br /> <br /> Zapisz typ każdego z opisanych poniżej błędów.<br /> <br /> ćwiczenie Brak nawiasów wokół warunku w instrukcji if. <br /> <br /> .......................................<br /> <br /> Pominięcie początkowego przypisania wartości 0 licznikowi pętli. <br /> <br /> .......................................<br /> <br /> Utworzenie pętli, która kontynuuje działanie po odczytaniu ostatniego elementu tablicy. <br /> <br /> .......................................<br /> <br /> Pominięcie zamykającego nawiasu klamrowego na końcu funkcji. <br /> <br /> .......................................<br /> <br /> jesteś tutaj  535<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zapisz typ każdego z opisanych poniżej błędów.<br /> <br /> ćwiczenie Brak nawiasów wokół warunku w instrukcji if. <br /> <br /> błąd składniowy ...................................................<br /> <br /> Pominięcie początkowego przypisania wartości 0 licznikowi pętli. <br /> <br /> błąd logiczny ...................................................<br /> <br /> Utworzenie pętli, która kontynuuje działanie po odczytaniu ostatniego elementu tablicy. <br /> <br /> błąd czasu wykonywania ...................................................<br /> <br /> Pominięcie zamykającego nawiasu klamrowego na końcu funkcji. <br /> <br /> błąd składniowy ...................................................<br /> <br /> Numer rejestrowanej rozmowy jest wyświetlany jako „nie liczba” (NaN). Bardzo to dziwne…<br /> <br /> Że to niby nie jest liczba? Skoro w końcu udało się nam uruchomić konsolę do testowania, możemy zająć się sprawdzeniem, co się dzieje ze zmienną callNum, i to bez konieczności denerwowania się na te wszystkie wyskakujące okienka dialogowe. I co się okazało…? Otóż okazało się, że stary problem, który Olek początkowo zignorował, w końcu postanowił się ujawnić. Zmienna callNum jest prezentowana jako NaN, co oznacza, że jej wartość nie jest liczbą. Ale dlaczego? console.displayMsg("callNum: " + callNum);<br /> <br /> Jeden wiersz kodu wystarczy, by zażądać wyświetlenia wartości zmiennej callNum.<br /> <br /> Przynajmniej konsola do testowania działa tak, jak należy!<br /> <br /> 536<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Kiedy sama obserwacja nie wystarcza Czasami bywa i tak, że samo obserwowanie zmiennych nasunie więcej pytań niż odpowiedzi. Dlaczego zmienna callNum nie jest liczbą? Dlaczego zmienna ta nie jest inkrementowana? Jaki jest cel stosowania konsoli błędów, jeśli jedynie potwierdza to, o czym wiedzieliśmy już wcześniej, a mianowicie to, że mamy problem? A zatem co możemy zrobić, aby precyzyjnie określić, na czym polega błąd?<br /> <br /> Co teraz?<br /> <br /> Pomocne może być eliminowanie kodu aż do momentu, gdy błąd pojawi się w innym wierszu.<br /> <br /> Usuwanie kodu jest doskonałym sposobem upraszczania skryptu w ramach jego uruchamiania. Jeśli chodzi o testowanie w JavaScripcie, to często „mniej” oznacza „więcej”. W naszym przypadku usuwanie kodu i obserwacja, co się w efekcie zmieniło, to doskonałe rozwiązanie. Jednak samego usuwania kodu nie można uznać za próbę jego naprawy, gdyż po zakończeniu testowania jego przeważająca większość pozostanie w takiej postaci, do jakiej go zmieniliśmy. Potrzebujemy zatem sposobu na wyłączanie go, tak by nie był wykonywany, lecz wciąż pozostawał w zawartości dokumentu HTML.<br /> <br /> jesteś tutaj  537<br /> <br /> Skrępowany komentarzem<br /> <br /> Komentarze jako chwilowe wyłączniki kodu Ukrycie wykonywanego kodu w komentarzu jest niezwykle prostym i użytecznym sposobem wyłączania kodu podczas testowania. W ten sposób można chwilowo zablokować wykonywanie wybranego fragmentu kodu, a przy tym wcale nie trzeba go usuwać. Technikę tę możesz sobie wyobrazić jako chwilowe usuwanie wierszy lub większych fragmentów kodu w celu określenia lokalizacji błędu. er, winningNum) { function checkWinner(form, call " + callNum); console.displayMsg("callNum: /*<br /> <br /> ów // Inkrementujemy liczbę rozm var callNum; ++callNum;<br /> <br /> Komentarze są niezwykle przydatnym sposobem chwilowego wyłączania kodu.<br /> <br /> Hej, teraz numer rozmowy zawsze ma wartość 0. A zatem to coś we fragmencie kodu, który został umieszczony w komentarzu, sprawia, że interpreter uznaje go za „nie liczbę”.<br /> <br /> // Sprawdzamy zwycięzcę i dzisiejszym zwycięzcą!"); if (callNum == winningNum) { ówcą numer " + callNum + "... alert(caller + ", jesteś rozm form.submit(); } else { nia rozmówcy // Czyścimy pole do podania imie ElementById('caller'); var callerField = document.get rozmówca"; callerField.value = "Następny callerField.focus(); callerField.select(); } */ }<br /> <br /> Wielowierszowe komentarze wyłączyły cały kod funkcji, za wyjątkiem jednego wiersza, który wyświetla komunikat testowy na konsoli.<br /> <br /> Teraz wartość zmiennej callNum wynosi 0, co oznacza, że to jakiś kod umieszczony w komentarzu jest przyczyną problemów.<br /> <br /> WYTĘŻ umysł<br /> <br /> Jak sądzisz, co się stanie, kiedy usuniemy z komentarza wyłącznie wiersz kodu inkrementujący wartość licznika rozmów, tak by ponownie był on wykonywany?<br /> <br /> 538<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Problem (jakby) rozwiązany Po skorzystaniu z komentarzy jednowierszowych można w bardziej precyzyjny sposób określić, jaki kod ma być wykonywany, a jaki pozostanie wyłączony. Po usunięciu komentarza z wiersza zawierającego inkrementację zmiennej callNum zaczęła ona funkcjonować zgodnie z oczekiwaniami. Można zatem wywnioskować, że problemy wywołuje jeden z wierszy kodu, który jeszcze jest umieszczony w komentarzu. Teraz zastosowaliśmy komentarze jednowierszowe, dzięki czemu bez problemów można włączać i wyłączać konkretne wiersze kodu.<br /> <br /> function checkWinner(form, caller, winningNum) { console.displayMsg("callNum: " + callNum); //<br /> <br /> // Inkrementujemy liczbę rozmów var callNum; ++callNum;<br /> <br /> Usunięcie komentarza z wiersza um inkrementującego zmienną callN sprawiło, że zaczęła ona działać zgodnie z oczekiwaniami.<br /> <br /> // Sprawdzamy zwycięzcę if (callNum == winningNum) { alert (caller + ", jesteś rozmówcą numer " + callNum + "... i dzisiejszym zwycięzcą!"); form.submit(); } else { // Czyścimy pole do podania imienia rozmówcy // var callerField = document.getElementById('caller'); // callerField.value = "Następny rozmówca"; // callerField.focus(); // callerField.select(); // } } // // // // //<br /> <br /> Zmienna callNum w koń jak powinna, czyli jes cu działa tak, t po odebraniu każdej rozinkrementowana mowy.<br /> <br /> Zaostrz ołówek Napisz, na czym polega problem związany ze zmienną callNum oraz jak można go poprawić. ........................................................................................................................................................................... ........................................................................................................................................................................... ........................................................................................................................................................................... ........................................................................................................................................................................... ...........................................................................................................................................................................<br /> <br /> jesteś tutaj  539<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Napisz, na czym polega problem związany ze zmienną callNum oraz jak można go poprawić.<br /> <br /> Przez pomyłkę wewnątrz funkcji checkWinner() tworzona jest nowa zmienna lokalna o nazwie ........................................................................................................................................................................... callNum (używamy przy tym słowa kluczowego var). To sprawia, że nowa zmienna lokalna ........................................................................................................................................................................... „przesłania” zmienną globalną o tej samej nazwie; w ten sposób powstaje subtelny i trudny ........................................................................................................................................................................... do wykrycia błąd. Ponieważ ta nowa zmienna lokalna nie jest inicjowana, zatem próba jej ........................................................................................................................................................................... inkrementacji i porównania ze zmienną określającą zwycięski numer rozmowy sprawia, że ........................................................................................................................................................................... zostaje ona potraktowane jako „nie liczba”. Rozwiązanie tego problemu sprowadza się do ........................................................................................................................................................................... usunięcia wiersza kodu, w którym tworzona jest zmienna lokalna callNum. ...........................................................................................................................................................................<br /> <br /> Usunięcie wiersza kodu tworzącego zmienną lokalną likwiduje także pluskwę numer 4.<br /> <br /> // Inkrementujemy liczbę rozmów var callNum; ++callNum;<br /> <br /> Usunięcie wiersza kodu, który przypadkowo tworzy zmienną lokalną o nazwie callNum, spowoduje, że funkcja checkWinner() ponownie zacznie korzystać ze zmiennej globalnej.<br /> <br /> Niebezpieczeństwa związane ze zmiennymi-cieniami Błąd związany ze zmienną callNum, który przed chwilą poprawiliśmy na stronie do rejestracji rozmów telefonicznych, jest przykładem tak zwanych zmiennych-cieni (ang. shadow variable), czyli zmiennych, które przez przypadek przesłaniają inne zmienne o tej samej nazwie. Problem ten powstaje w sytuacji, gdy zmienna lokalna ma tę samą nazwę co zmienna globalna. JavaScript tworzy zmienne lokalne i w lokalnym bloku kodu nadaje im wyższy priorytet niż zmiennym globalnym. Dlatego też żadne modyfikacje wartości zmiennej lokalnej nie mają wpływu na wartość zmiennej globalnej — innymi słowy, zmienna lokalna przesłania lub ukrywa w cieniu zmienną globalną, chwilowo uniemożliwiając skryptowi korzystanie z niej. Zmienna globalna.<br /> <br /> 5<br /> <br /> Zmienna lokalna.<br /> <br /> 0<br /> <br /> callNum<br /> <br /> Ta sama nazwa!<br /> <br /> Rozdział 11.<br /> <br /> ++callNum; Kod inkrementuje zmienną globalną, przez co przyjmuje ona wartość 6.<br /> <br /> callNum<br /> <br /> 540<br /> <br /> Kod globalny<br /> <br /> Zmienna-cień pojawia się w sytuacji, gdy w skrypcie zostaną utworzone zmienna globalna oraz zmienna lokalna o tej samej nazwie… Kiepska sprawa!<br /> <br /> Kod lokalny ++callNum;<br /> <br /> lną, Ten kod inkrementuje zmienną loka— 1 ość wart ona e jmuj przy co z prze i jej zmienna globalna jest przesłonięta wartość nie zostaje zmieniona.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Nie ma<br /> <br /> niemądrych pytań<br /> <br /> P: Kiedy dodaję do kodu komentarze, by znaleźć<br /> <br /> błąd, skąd mam wiedzieć, ile kodu należy wyłączyć?<br /> <br /> O: To kwestia wyczucia, a tym lepiej będziesz to w stanie ocenić, im<br /> <br /> większe będziesz mieć doświadczenie w testowaniu kodu JavaScript. Nigdy jednak nie zaszkodzi umieścić w komentarzach jak największą część, a może nawet cały kod otaczający miejsce, w którym występują problemy. A jeśli przydarzy Ci się naprawdę paskudny błąd, to nie wahaj się i umieść w komentarzu cały kod JavaScript na stronie. Nie zapomnij także chwilowo zablokować znaczników importujących na stronę kod z zewnętrznych plików JavaScript. Istnieje jeszcze jedna technika, którą możesz zastosować, jeśli już udało Ci się zawęzić potencjalne źródło problemów do pewnego fragmentu skryptu. Polega ona na umieszczaniu w komentarzu kolejnych wierszy kodu tak długo, aż błąd zniknie. A zatem, zamiast umieszczać w komentarzu cały blok kodu, a następnie stopniowo dołączać kolejne wiersze do wykonywanego skryptu i obserwować, kiedy błąd się pojawi, możemy stopniowo dodawać do komentarza<br /> <br /> kolejne wiersze kodu i obserwować, kiedy błąd zniknie. To pierwsze rozwiązanie jest lepsze w sytuacji, gdy nie mamy żadnych podpowiedzi ani przypuszczeń dotyczących lokalizacji problematycznego kodu; natomiast drugie daje lepsze rezultaty, kiedy miejsce występowania błędu jest już w jakimś stopniu określone.<br /> <br /> P: A co, jeśli specjalnie chcę utworzyć zmienną-cień?<br /> <br /> Czy w takim przypadku wszystko będzie w porządku?<br /> <br /> O: To tak, jakbyś planował celowo złamać nogę… czy to jest<br /> <br /> słuszne? Oczywiście, że nie. Sam fakt celowego zadawania sobie bólu i cierpienia nie czyni go w żadnej mierze bardziej znośnym. Poza tym wystarczająco dużo cierpień i przykrości przysporzy nam testowanie kodu, który powinien działać prawidłowo, więc nie ma sensu strzelać sobie w stopę i celowo zwiększać współczynnik zagrożenia. A zatem, odpowiadając na Twoje pytanie, należy stwierdzić, że zmienne-cienie wprowadzają tak wielkie zamieszanie w kodzie i tak trudno jest znaleźć przyczynę problemów, jeśli się pojawią, że należy ich unikać zawsze i we wszystkich sytuacjach.<br /> <br /> Sprawa zamknięta! Dzięki dużej dozie cierpliwości i przy wykorzystaniu nowych umiejętności w dziedzinie testowania skryptów Olkowi udało się zamknąć sprawę i awansować na stanowisko detektywa do spraw kodu JavaScript.<br /> <br /> I kolejna sprawa zamknięta!<br /> <br /> Detektyw Olek zaprawiony w testowaniu kodu JavaScript.<br /> <br /> Prawidłowo działająca strona rejestrująca połączenia wraz z dołączoną do niej konsolą test ową<br /> <br /> .<br /> <br /> jesteś tutaj  541<br /> <br /> Podręczna lista unikania błędów<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> Lista zasad „odpluskwiania” według Olka<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> . ................................................... ją w parach ............ Upewnij się, że nawiasy zawsze występu............<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> ją ............... występu ............ zawsze ............ kodu ............ ące bloki ............ ............ Upewnij się, że nawiasy klamrowe otaczaj pomóc ........................... może ............ ............ ów klamro ............wych nawias par ............ waniu ............ ............ w odnajdy — ............ ............ w parach ............ ........................ kodzie. ........................................................................... wcięć w............ ednich............ odpowi............ nie ............ stosowa ............ ........................<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> katorów ........................... h identyfi ............ nazwac ............ ek w............ ............ Ze wszystkich sił staraj się unikać literów ów, jeśli ............... problem ............ sporo............ rzać............ przyspa ............ mogą ............ i funkcje ............ e, jak............ o zmienn ............ — zarówn ........................ ........................ sam sposób. ....................................... w taki............ ane............ zapisyw ............ entnie ............ konsekw ............ nie będą............ ich nazwy ........................ ........................<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> lną uwagę, ........................... szczegó............ y; zwraca ............j............ ............ Konsekwentnie stosuj cudzysłowy i apostrof ................................................... je na przemi ............an. ć ............ stosowa ............ ów HTML ............ atrybut............ kodzie............ by w............ ........................<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> znaki czać ............... ............ ipt umiesz ............ JavaScr ............ znaków............ Stosuj znaki unikowe, by w łańcuchach............ y (\')............................ apostrof ............ (\") lub............ owy ............ cudzysł ............ jak............ iu, takie ............ znaczen ............ lnym ............ o specja ............ ........................<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> Ci ... chodzi............ gdy............ ci (=), ............ równoś ............ znaku ............ ............ Nigdy, ale to przenigdy nie używaj samego za błąd, ............... uzna tego ............ ipt nie ............ JavaScr............ niej ............ dopodob ............ Najpraw ............ (==)............. ............ r równoś ............ci o operato ............ ........................ sposób. ................................................... ............ w zamier ............zony działał ............ będzie ............ kod nie ............ Twój............ jednak ............ ........................<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> odwołać............... niego ............ się do............ esz ............ spróbuj ............ ............ Upewnij się, że obiekt jest utworzony, zanim one przed ............... utworz ............ nie będą ............ które ............ stron,............ tów ............ elemen lności ............ ............ to w szczegó ............ — dotyczy ........................ ........................ ............... ia onload. .................................................................................... zdarzen ............ niem............ zgłosze ............ ........................<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> j, gdyż ........................... i lokalne ............ ............ ej globaln ............ej ............ Nigdy nie nadawaj tej samej nazwy zmienn do ............... adzić............ doprow ............ co może ............ ą,............ ą globaln ............ ni zmienn ............ ........................ a lokalna ............przesło zmienn ............ ........................ . ........................................................................... ia skryptu ............ działan ............ nego............ widzia ............ nieprze ............ ........................<br /> <br /> ....................................................................................<br /> <br /> ...........................................................................<br /> <br /> ....................................................................................<br /> <br /> 542<br /> <br /> Rozdział 11.<br /> <br /> Zabijaj pluskwy — na śmierć!<br /> <br /> Zaginarka stron Zegnij stronę wzdłuż pionowych linii, tak by oba mózgi się połączyły, a następnie rozwiąż zagadkę.<br /> <br /> Na co zasługują wszystkie pluskwy w kodzie JavaScript? To spotkanie umysłów!<br /> <br /> Sprawy zamknię te!<br /> <br /> Spr<br /> <br /> awa nr 1 ZAMK NIĘTA Ale odlot… Hej, ale ja nie jestem pluskwą! Powaga!<br /> <br /> Sprawa nr 2<br /> <br /> ĘTA I N MK<br /> <br /> ZA<br /> <br /> Jedno wiadomo na pewno: w przypadku pluskiew, nadstawianie drugiego policzka to całkowite nieporozumienie, które, w efekcie, może obniżyć próg tolerancji na błędy. Zwróć uwagę, że usunięcie objawów błędów to jeszcze nie wszystko — trzeba zrozumieć i wyeliminować ich przyczyny.<br /> <br /> jesteś tutaj  543<br /> <br /> 544<br /> <br /> Rozdział 11.<br /> <br /> 12. Dynamiczne dane<br /> <br /> Szybkie i wrażliwe aplikacje internetowe Niech moje oczy cię nie zmylą. Pod tą ładną buzią kryją się szalejące uczucia, które chcą się uwolnić. Prawdę mówiąc, dynamiczna osobowość jest moim największym skarbem.<br /> <br /> W nowoczesnym internecie bardzo duże znaczenie ma szybkość i trafność reakcji — powszechnie oczekuje się, że strony będą błyskawicznie reagować na każdą zachciankę użytkownika. Albo przynajmniej marzą o tym niektórzy użytkownicy i twórcy aplikacji<br /> <br /> internetowych. Ważną rolę w realizacji tego marzenia odgrywa język JavaScript, stanowiący kluczowy element techniki o nazwie Ajax, zapewniającej możliwość dynamicznego modyfikowania zawartości i wyglądu stron WWW. Dzięki Ajaksowi strony WWW w znacznie większym stopniu przypominają standardowe aplikacje komputerowe, gdyż mogą szybko i dynamicznie pobierać oraz zapisywać dane, błyskawicznie odpowiadając na poczynania użytkownika i to bez przeładowywania stron.<br /> <br /> to jest nowy rozdział  545<br /> <br /> Chcę więcej… dynamiczne dane<br /> <br /> Pożądając dynamicznych danych Czy pamiętasz jeszcze Renię, blogerkę i pasjonatkę sześciennych układanek? Renia uwielbia swoją stronę o nazwie MagicznaKostka, jednak jest sfrustrowana koniecznością edytowania pliku HTML za każdym razem, gdy chce dodać nowy wpis do swojego blogu. Chciałaby mieć możliwość całkowitego odseparowania wpisów w blogu od kodu HTML opisującego stronę, gdyż dzięki temu mogłaby się skoncentrować na samych wpisach.<br /> <br /> Dodawanie nowych wpisów do blogu nie powinno wymagać edycji strony MagicznaKostka.<br /> <br /> Naprawdę bardzo bym chciała móc dodawać nowe wpisy do blogu bez konieczności edytowania kodu HTML.<br /> <br /> Renia — sfrustrowana blogerka i układankowa marzycielka.<br /> <br /> 546<br /> <br /> Rozdział 12.<br /> <br /> Dodanie nowego wpisu do blogu zmusza Renię do edycji kodu HTML strony MagicznaKostka.<br /> <br /> Dynamiczne dane<br /> <br /> MagicznaKostka sterowana danymi Renia planuje coś zmienić. Nowa wersja jej blogu, w którym wpisy mają być oddzielone od struktury dokumentu HTML, wymaga zastosowania dynamicznych danych, czyli danych, które są dynamicznie wczytywane i przetwarzane przez przeglądarkę. Takie strony WWW, bazujące na dynamicznych danych, określa się jako strony sterowne danymi (ang. data-driven). Nazwa ta odpowiada sposobowi działania strony, gdyż ta definiuje jedynie pewną strukturę, która jest następnie wypełniana danymi. Innymi słowy, to dane odpowiadają za określenie treści strony.<br /> <br /> Dane blogu są przechowywane w osobnym pliku, który można edytować niezależnie od strony WWW.<br /> <br /> Dane blogu<br /> <br /> <?xml version="1.0"?> <blog> <title>MagicznaKostka - blog miłośników przestrzennych układanek</t itle> <author rel="nofollow">Renia - miłośniczka układanek</a uthor> <entries><br /> <br /> Strona WWW<br /> <br /> <entry> <date>08/14/2008</date> <body>Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko.</body> </entry> <entry> <date>08/19/2008</date> <body>Udało mi się już ułożyć tę nową układankę. Już mnie znudziła, więc szukam czegoś nowego.</bod </entry> y> <entry> <date>08/16/2008</date> <body>Już mnie głowa rozbolała od zabaw tą nową kostką. Muszę sobie odpocząć.</b </entry> ody> <entry> <date>08/21/2008</date> <body>Znalazłam w internecie układankę 7x7x7. O rany! Zabawa będzie super.</body> </entry> <entry> <date>08/29/2008</date> <body>Spotkałam się z kilkoma innymi zapaleńcami, by porozmawiać o układaniu kostki 7x7x7. I szczerze </entry> mówiąc, mam mieszane uczucia.</bo dy> <entry> <date>08/01/2008</date> <body>Zdecydowałam się zamówić tę przerażającą kostkę 7x7x7. Już zaczynam się duchowo przygotowywa </entry> ć na jej układanie.</body> <entry> <date>09/3/2008</date> <body>Brałam udział w wyścigu przed lokalnym sklepem z zabawkami, który przestał sprzedawać układanki. </entry> Miłośnicy układanek — moc jest z nami!</body> <entry><br /> <br /> <html> <head> itle> miłośników przestrzennych układanek</t <title>MagicznaKostka - blog <script type="text/javascript"> dat w formacie DD/MM/RRRR // Metoda obiektu Date do wyświetlania function() { Date.prototype.shortFormat = this.getFullYear(); (this.getMonth() + 1) + "/" + return this.getDate() + "/" + } // Konstruktor obiektu Blog { function Blog(body, date, image) // Przypisanie wartości this.body = body; this.date = date; this.image = image; } ... </head> <body onload="showBlog(5);"> przestrzennych układanek</h3> <h3>MagicznaKostka - blog miłośników " /> <img src="cube.png" alt="YouCube rchBlog();" /> value="Przeszukaj blog" onclick="sea <input type="button" id="search" text" value="" /> name="search xt" id="searchte <input type="text" <div id="blog"></div> onclick="showBlog();" /> value="Pokaż wszystkie wpisy" <input type="button" id="showall" onclick="randomBlog();" /> om" value="Pokaż losowy wpis" <input type="button" id="viewrand </body> </html><br /> <br /> JavaScript jest odpowiedzialny za przetwarzanie danych blogu i umieszczanie ich na wynikowej stronie WWW.<br /> <br /> Strona WWW zawiera kod HTML określający jej strukturę oraz kod JavaScript niezbędny do wyświetlenia na stronie dynamicznych danych blogu.<br /> <br /> Dane wpisów są wczytywane z osobnego pliku.<br /> <br /> Pliki nowej wersji strony MagicznaKostka można znaleźć na on, serwerze FTP wydawnictwa Heli zip. ftp://ftp.helion.pl/przyklady/hfjsc.<br /> <br /> <date>08/5/2008</date> <body>Dostałam zamówioną kostkę 7x7x7 — przez jakiś czas pewnie będę milczeć.</body> </entry> <entry> <date>09/19/2008</date> <body>O rany... zajęło mi to prawie dwa tygodnie, ale w końcu udało mi się ułożyć tę kostkę!</bod <image>cube777.png</image> y> </entry> <entry> <date>09/24/008</date> <body>Śniło mi się dziś w nocy, że goni mnie ogromna kostka... Brrrr!</body> </entry> <entry> <date>09/26/2008</date> <body>Sny stają się coraz dziwniejsze. .. Teraz widzę, że kostka rozpada się na części. Co <strong>to</ <image>cubeapart.png</image> strong> znaczy?</body> </entry> </entries> </blog><br /> <br /> Dzięki zastosowaniu JavaScriptu dane blogu są dynamicznie scalane z kodem HTML, tworząc w ten sposób wynikową stronę MagicznaKostka, która wygląda tak samo jak strona oryginalna. Jednak ta nowa, sterowana danymi strona składa się z dwóch niezależnych części: kodu HTML określającego strukturę strony oraz danych blogu. Dzięki zapisaniu danych blogu w niezależnym pliku Renia może do woli modyfikować treść poszczególnych wpisów i nie będzie to miało żadnego wpływu na kod HTML, CSS ani JavaScript używany na stronie.<br /> <br /> =<br /> <br /> <html> <head> ... </head> <body> ... </body> </html><br /> <br /> youcube.html<br /> <br /> +<br /> <br /> <blog> <title>... <author rel="nofollow">... <entries> <entry> ... </entry> ... </entries> </blog><br /> <br /> Aby zaktualizować nową, dynamiczną wersję swojego blogu, Renia musi wprowadzać zmiany wyłącznie w tym pliku.<br /> <br /> blog.xml<br /> <br /> jesteś tutaj  547<br /> <br /> Przygotowania oszczędzą nam pracy w przyszłości<br /> <br /> Dynamiczne dane — to musi być skomplikowane. Założę się, że ich obsługa wymaga napisania masy złożonego kodu JavaScript, prawda?<br /> <br /> Korzystanie z dynamicznych danych wymaga nieco więcej wysiłku na początku, lecz później zapewnia niewspółmiernie duże korzyści. Choć nie ma wątpliwości, że stworzenie strony korzystającej z dynamicznych danych na początkowym etapie prac wymaga dodatkowego planowania i wysiłku programistycznego, to jednak w dłuższej perspektywie ten nakład pracy zwraca się z nawiązką dzięki możliwości znacznie prostszej i szybszej aktualizacji treści strony. Poza tym JavaScript posiada wbudowane mechanizmy wspomagające korzystanie z dynamicznych danych dzięki sprytnej metodologii o nazwie Ajax.<br /> <br /> 548<br /> <br /> Rozdział 12.<br /> <br /> Dynamiczne dane<br /> <br /> Ajax oznacza komunikację Ajax umożliwia korzystanie z dynamicznych danych dzięki zapewnieniu możliwości prowadzenia niewielkich „konwersacji” pomiędzy przeglądarką i serwerem WWW. Konkretnie rzecz biorąc, skrypt może poprosić serwer o pewne dane, takie jak wpisy w blogu, a serwer dzięki technice Ajax może mu je odesłać. Następnie skrypt przetwarza odebrane dane wpisów i wyświetla je na stronie.<br /> <br /> Ajax zapewnia stronom możliwość dynamicznego pobierania danych z serwera.<br /> <br /> wyświetlona Strona WWW e, korzystając z  techniki rc da lą eg rz cenia w p serwera zwró Ajax, żąda od ch. pewnych dany<br /> <br /> Klient inicjuje żądanie, a następnie czeka na odpowiedź.<br /> <br /> <html> <head> ... </head> <body> ... </body> </html><br /> <br /> JavaScript pełni w tej procedurze rolę pośrednika, który odpowiada za zainicjowanie żądania, obsługę odpowiedzi oraz wyświetlenie danych na stronie WWW.<br /> <br /> Przeglądarka WWW.<br /> <br /> youcube.html<br /> <br /> Klient Serwer odpowiada, przesyłając do klienta dane blogu.<br /> <br /> Po odebraniu odpowiedzi z serwera klient odczytuje dane blogu i natychmiast wyświetla je na stronie, bez konieczności jej przeładowywania.<br /> <br /> Serwer<br /> <br /> <blog> <title>... <author rel="nofollow">... <entries> <entry> ... </entry> ... </entries> </blog><br /> <br /> Serwer odbiera żądanie i odpowiada na nie, zwracając dane blogu.<br /> <br /> blog.xml<br /> <br /> WYTĘŻ umysł<br /> <br /> Co w kontekście danych blogu oznacza „XML”? Czy sądzisz, że zastosowanie XML-a ułatwia korzystanie z dynamicznych danych blogu?<br /> <br /> jesteś tutaj  549<br /> <br /> HTML pod inną nazwą<br /> <br /> XML — HTML na każdą okazję Litery „ML” w nazwie HTML pochodzą od angielskich słów „markup language” (język znaczników), co ma związek z tym, że HTML używa znaczników i atrybutów do tworzenia tak zwanego „hipertekstu” (ang. hypertext, od czego pochodzą dwie pierwsze litery skrótu: „HT”). XML, podobnie jak HTML, jest kolejnym językiem znaczników, lecz w odróżnieniu od HTML-a, który służy wyłącznie do tworzenia stron WWW, XML-a można użyć… do wszystkiego, co nam przyjdzie do głowy. To właśnie te nieograniczone możliwości rozszerzania i adaptacji symbolizuje literka „X” w nazwie XML. Pomysł polega na tym, by można było skorzystać z możliwości przechowywania danych w postaci znaczników i atrybutów. Dlaczego zatem nie można by rozszerzyć zakresu języka znaczników, tak by rozwiązywał on także inne problemy związane z danymi? <html> itle> <head> miłośników przestrzennych układanek</t <title>MagicznaKostka - blog <script type="text/javascript"> dat w formacie DD/MM/RRRR // Metoda obiektu Date do wyświetlania { function() Date.prototype.shortFormat = this.getFullYear(); (this.getMonth() + 1) + "/" + return this.getDate() + "/" + }<br /> <br /> HTML<br /> <br /> // Konstruktor obiektu Blog { function Blog(body, date, image) // Przypisanie wartości this.body = body; this.date = date; this.image = image; } ...<br /> <br /> Strona WWW<br /> <br /> </head> <body onload="showBlog(5);"> przestrzennych układanek</h3> <h3>MagicznaKostka - blog miłośników " /> rchBlog();" /> <img src="cube.png" alt="YouCube value="Przeszukaj blog" onclick="sea <input type="button" id="search" /> xt" name="searchtext" value="" <input type="text" id="searchte <div id="blog"></div> onclick="showBlog();" /> value="Pokaż wszystkie wpisy" id="showall" " type="button <input onclick="randomBlog();" /> om" value="Pokaż losowy wpis" <input type="button" id="viewrand </body> </html><br /> <br /> <?xml version="1.0"?> <blog> <title>MagicznaKostka - blog miłośników <author rel="nofollow">Renia - miłośniczka układanek</a przestrzennych układanek uthor> 08/14/2008 Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko. 08/19/2008 Udało mi się już ułożyć tę nową układankę. Już mnie znudziła, więc szukam czegoś nowego. 08/16/2008 Już mnie głowa rozbolała od zabaw tą nową kostką. Muszę sobie odpocząć. 08/21/2008 Znalazłam w internecie układankę 7x7x7. O rany! Zabawa będzie super. 08/29/2008 Spotkałam się z kilkoma innymi zapaleńcami, by porozmawiać o układaniu kostki 7x7x7. I szczerze mówiąc, mam mieszane uczucia. dy> 08/01/2008 Zdecydowałam się zamówić tę przerażającą kostkę 7x7x7. Już zaczynam się duchowo przygotowywa ć na jej układanie.

XML

09/3/2008 Brałam udział w wyścigu przed lokalnym sklepem z zabawkami,
który przestał sprzedawać układanki. Miłośnicy układanek — moc jest z nami! 08/5/2008 Dostałam zamówioną kostkę 7x7x7 — przez jakiś czas pewnie będę milczeć. 09/19/2008 O rany... zajęło mi to prawie dwa tygodnie, ale w końcu udało mi się ułożyć tę kostkę!cube777.png y> 09/24/008 Śniło mi się dziś w nocy, że goni mnie ogromna kostka... Brrrr! 09/26/2008 Sny stają się coraz dziwniejsze. .. Teraz widzę, że kostka rozpada cubeapart.png się na części. Co to znaczy?


Transakcja handlowa

Lista piosenek

Wpisy w blogu

Czynnikiem, który sprawia, że XML jest tak potężny, jest jego elastyczność. W odróżnieniu od HTML-a, który zawiera ściśle określony zestaw znaczników i atrybutów, XML w ogóle ich nie definiuje — określa natomiast zestaw reguł, według których znaczniki i atrybuty mają być tworzone i używane. To każde zastosowanie (tak zwana aplikacja) XML-a ma określić wszystkie szczegóły znaczników i atrybutów opisujących używane dane.

550

Rozdział 12.

XML to język znaczników, służący do zapisywania dowolnych danych.

Dynamiczne dane

XML pozwala nam opisywać nasze dane po swojemu Prawdziwe piękno XML-a polega na tym, że dzięki niemu każdy może stać się panem i stwórcą własnych znaczników — wystarczy nieco znacznikowej alchemii, by przygotować całkowicie nowy i niepowtarzalny język znaczników, doskonale dostosowany do naszych potrzeb. Bez wątpienia istnieje już dużo gotowych języków XML, utworzonych w celu rozwiązania wielu różnych problemów. Jeśli zdarzy się, że jeden z takich języków spełnia nasze potrzeby, to zastosowanie go wcale nie będzie złym pomysłem. Choć z drugiej strony stworzenie własnego języka znaczników jest niewątpliwie pokusą, której trudno się oprzeć.

dobnie Ten kod XML, po łada się sk , ML HT d jak ko icznej z pewnej hierarch ów. nt me ele struktury

Gleaming the Cube

Każda informacja o filmie jest przechowywana w osobnym znaczniku.

13/01/1989 Graeme Clifford

Skatebordzista bada okoliczności śmierci swojego przyrodniego brata


Chociaż na pewno nigdy wcześniej nie widziałeś takiego przykładu języka XML, gdyż został on wymyślony na potrzeby tej książki, to jednak opisowe znaczniki sprawiają, że zrozumienie zawartości kodu nie jest żadnym problemem. Co ważniejsze, znaczniki bardzo dokładnie odpowiadają informacjom, które są w nich przechowywane — to po prostu samo się narzuca, czy personalia reżysera filmu zapisać w znaczniku !

ćwiczenie

Informacje o filmie są zapisane w znaczniku .

Dopasuj podanie poniżej znaczniki do opisów, a następnie przy każdym ze znaczników zapisz, czy jest to znacznik XML, czy HTML.

.....................



Wytłuszczony tekst na stronie WWW.

.....................



Tytuł internetowego kanału informacyjnego.

.....................

<br /> <br /> Pole do wprowadzania danych na stronie WWW.<br /> <br /> .....................<br /> <br /> <strong><br /> <br /> Tekst konwertowany na mowę w czasie rozmowy telefonicznej.<br /> <br /> .....................<br /> <br /> <input><br /> <br /> Twórca podcastu iTunes.<br /> <br /> .....................<br /> <br /> <prompt><br /> <br /> Wewnątrzwierszowa zawartość na stronie WWW.<br /> <br /> jesteś tutaj  551<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> ćwiczenie Rozwiązanie XML ..................... HTML .....................<br /> <br /> Dopasuj podanie poniżej znaczniki do opisów, a następnie przy każdym ze znaczników zapisz, czy jest to znacznik XML, czy HTML.<br /> <br /> <itunes:author><br /> <br /> Wytłuszczony tekst na stronie WWW.<br /> <br /> <span><br /> <br /> Tytuł internetowego kanału informacyjnego.<br /> <br /> XML ..................... HTML .....................<br /> <br /> <title><br /> <br /> Pole do wprowadzania danych na stronie WWW.<br /> <br /> <strong><br /> <br /> Tekst konwertowany na mowę w czasie rozmowy telefonicznej.<br /> <br /> HTML ..................... XML .....................<br /> <br /> <input><br /> <br /> Twórca podcastu iTunes.<br /> <br /> <prompt><br /> <br /> Wewnątrzwierszowa zawartość na stronie WWW.<br /> <br /> XML to zwyczajny tekst Dane XML, podobnie jak kod HTML, to zwyczajny tekst. Oznacza to, że są one zapisywane w zwyczajnym pliku tekstowym. Niemniej pliki XML zazwyczaj mają rozszerzenie .xml, co odróżnia je od plików HTML, które zwyczajowo mają rozszerzenie .html.<br /> <br /> A zatem nowa, sterowana danymi wersja strony MagicznaKostka może być aktualizowana poprzez wprowadzenie zmian w pliku XML… Ale super!<br /> <br /> Dane XML są zazwyczaj zapisywane w plikach z rozszerzeniem XML.<br /> <br /> <?xml version="1.0"?> <blog> <title>MagicznaKostka - blog miłośników <author rel="nofollow">Renia - miłośniczka układanek</a przestrzennych układanek uthor> 08/14/2008 Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko. 08/19/2008 Udało mi się już ułożyć tę nową układankę. Już mnie znudziła, więc szukam czegoś nowego. 08/16/2008 Już mnie głowa rozbolała od zabaw tą nową kostką. Muszę sobie odpocząć. 08/21/2008 Znalazłam w internecie układankę 7x7x7. O rany! Zabawa będzie super. 08/29/2008 Spotkałam się z kilkoma innymi zapaleńcami, by porozmawiać o układaniu kostki 7x7x7. I szczerze mówiąc, mam mieszane uczucia. dy> 08/01/2008 Zdecydowałam się zamówić tę przerażającą kostkę 7x7x7. Już zaczynam się duchowo przygotowywa ć na jej układanie.

XML

09/3/2008 Brałam udział w wyścigu przed lokalnym sklepem z zabawkami,
który przestał sprzedawać układanki. Miłośnicy układanek — moc jest z nami! 08/5/2008 Dostałam zamówioną kostkę 7x7x7 — przez jakiś czas pewnie będę milczeć. 09/19/2008 O rany... zajęło mi to prawie dwa tygodnie, ale w końcu udało mi się ułożyć tę kostkę!cube777.png y> 09/24/008 Śniło mi się dziś w nocy, że goni mnie ogromna kostka... Brrrr! 09/26/2008 Sny stają się coraz dziwniejsze. .. Teraz widzę, że kostka rozpada cubeapart.png się na części. Co to znaczy?


552

Rozdział 12.

... <author rel="nofollow">... <entries> <entry> ... </entry> ... </entries> </blog><br /> <br /> blog.xml<br /> <br /> Dynamiczne dane<br /> <br /> XML + HTML = XHTML Być może XML i HTML różnią się używanymi rozszerzeniami nazw plików, jednak jest coś, co bardzo mocno wiąże ze sobą te dwa języki znaczników; tym czymś jest XHTML. XHTML jest nowoczesną wersją języka HTML, zgodną ze ścisłymi regułami XML-a. Na przykład w XHTML-u wszystkie znaczniki otwierające umieszczone na stronie muszą mieć odpowiadające im znaczniki zamykające. Składnia HTML-a nie jest ścisła i potrafi wiele „wybaczyć”, co oznacza, że na przykład pominięcie zamykającego znacznika </p> nie przysporzy nam żadnych problemów. XHTML jest zupełnie inny i wymaga, by wszystkie znaczniki były podawane w parach — znacznik otwierający i znacznik zamykający.<br /> <br /> HTML <p>To akapit tekstu w HTML-u. W HTML-u często używa się sam znacznika <p>, by oznaczyć pocz ego ątek nowego akapitu tekstu.<br /> <br /> XHTML jest wersją języka HTML, zgodną ze ścisłymi regułami składni XML-a.<br /> <br /> XHTML <p>To akapit tekstu w XHTML-u.</p> W XHTML-u znaczniki zawierające tekst zawsze muszą występować w parach.<br /> <br /> Kolejną ważną różnicą pomiędzy XHTML-em i HTML-em są znaczniki puste, takie jak <br>. W XHTML-u po nazwie znacznika należy zapisać znak odstępu oraz ukośnik, który symbolizuje znacznik zamykający.<br /> <br /> HTML<br /> <br /> XHTML<br /> <br /> To tylko zdanie.<br><br /> <br /> To tylko zdanie.<br /><br /> <br /> W HTML-u pusty znacznik br jest zazwyczaj zapisany bez znaku ukośnika.<br /> <br /> W XHTML-u w pustych znacznikach trzeba zapisywać znak odstępu i ukośnika.<br /> <br /> Jedną z najważniejszych różnic pomiędzy HTML-em i XHTML-em jest to, że XHTML wymaga, by wszystkie wartości atrybutów były zapisywane w cudzysłowach.<br /> <br /> HTML <a href=home.html rel="nofollow">Strona główna</a> Wartość atrybutu nie jest zapisana w cudzysłowach, co w najmniejszym stopniu nie narusza zasad składni języka HTML.<br /> <br /> XHTML <a href="home.html" rel="nofollow">Strona główna</a> W XHMTL-u wszystkie wartości atrybutów muszą być zapisywane w cudzysłowach.<br /> <br /> Choć XHTML nie spełnia potrzeb Reni w zakresie możliwości odwzorowania danych blogu w postaci danych XML, to jednak powyższe przykłady pokazują kilka najważniejszych zasad składni XML-a, które muszą być stosowane we wszystkich językach bazujących na XML-u, w tym także w języku Reni służącym do opisu zawartości blogu.<br /> <br /> jesteś tutaj  553<br /> <br /> XHTML kontra XML<br /> <br /> Pogawędki przy kominku<br /> <br /> Temat dzisiejszej pogawędki: HTML i XML rzucają nowe światło na internetowe dane.<br /> <br /> została Najnowsza wersja języka HTML -a XML iu użyc przy ana ułow form prze i nosi nazwę XHTML.<br /> <br /> HTML:<br /> <br /> XML:<br /> <br /> Wiesz co, naprawdę utrudniłeś mi życie. To ja stanowię podstawę WWW, a przez ciebie wiele osób nie wie, co ma o mnie myśleć. To nie moja wina, że masz klapki na oczach i myślisz jedynie o swoich stronach WWW. Ja jestem wizjonerem — mogę sobie wyobrazić i przedstawić dowolne dane. Ale beze mnie i tak sobie nie poradzisz, bo przeglądarki mogą wyświetlać wyłącznie kod HTML. Nawet nie wiedzą, jak by się miały zabrać do pokazywania twojej zawartości. He, he, bo ja jestem tajemniczy. Prawda jest taka, że jestem człowiekiem bez twarzy — esencją danych bez żadnej fizycznej prezentacji. Po prostu, aby się ujawnić, potrzebuję pewnej pomocy. Jak to możliwe? Kogo by interesowały dane, których nie można pokazać? Rany, gościu… czy ty naprawdę nic nie rozumiesz? Cały świat oprócz ciebie operuje na danych, których w większości przypadków nie widać. A transakcje bankowe, a ankiety wyborcze, warunki pogodowe… wystarczą ci te przykłady? Dzięki mnie można przedstawić wszystkie te informacje — wszystko tam jest… na WWW. To prawda, ale wysil się trochę i pomyśl, w jaki sposób te wszystkie informacje są zapisywane i przechowywane, zanim trafią do przeglądarki? Bynajmniej nie jako akapity i tabele, możesz mi wierzyć. Bardzo często dane te są przechowywane przeze mnie, ponieważ potrafię zagwarantować im odpowiednią strukturę i kontekst — ja po porostu gwarantuję łatwość przetwarzania danych. Rozumiem. Czy zatem sugerujesz, że tak naprawdę to my współpracujemy ze sobą? Właśnie tak! Ja nie mam pojęcia, jak dane wyglądają — koncentruję się na ich znaczeniu. Tak długo, jak ludzie będą używali przeglądarek WWW, będę potrzebował twojej pomocy przy prezentowaniu danych, które reprezentuję. Super… co za ulga!<br /> <br /> 554<br /> <br /> Rozdział 12.<br /> <br /> Dynamiczne dane<br /> <br /> XML i dane blogu Reni XHTML jest doskonałą aplikacją XML, która w bardzo szybki sposób poprawia strukturę i czytelność stron WWW. Wracając jednak do strony MagicznaKostka i blogu Reni, okazuje się, że XHTML nie wystarcza — Renia potrzebuje XML-a, by odpowiednio zamodelować bardzo specyficzne dane blogu. Musimy zatem zastanowić się, jakie unikalne informacje składają się na całość danych blogu oraz jak można je dopasować do hierarchicznych znaczników XML. <blog> <title><br /> <br /> MagicznaKostka – blog miłośników przestrzennych układanek<br /> <br /> <author rel="nofollow"><br /> <br /> Blog.prototype.signature = “Renia – miłośniczka układanek”;<br /> <br /> <entries> <entry> blog[0] = new Blog(“Dostałam właśnie nową, zamówioną...”,<br /> <br /> <date><br /> <br /> new Date(“08/14/2008”));<br /> <br /> <body><br /> <br /> Zaostrz ołówek Wymyśl swój własny język XML służący do przechowywania danych blogu, a następnie użyj go do zapisania jednego z wpisów blogu Reni. Zastanów się na zastosowaniem takich znaczników jak: title (tytuł), date (data), author (autor) oraz entry (wpis). <blog> ................................................................................... ................................................................................... ................................................................................... ................................................................................... ................................................................................... ................................................................................... ................................................................................... </blog><br /> <br /> jesteś tutaj  555<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Wymyśl swój własny język XML służący do przechowywania danych blogu, a następnie użyj go do zapisania jednego z wpisów blogu Reni. Zastanów się na zastosowaniem takich znaczników jak: title (tytuł), date (data), author (autor) oraz entry (wpis).<br /> <br /> <blog> Cały blog jest umieszczony wewnątrz znacznika <blog>.<br /> <br /> <title>MagicznaKostka – blog miłośników przestrzennych układanek ................................................................................... Znacznik <author rel="nofollow">Renia – miłośniczka układanek</author> ................................................................................... era tytuł blogu. zawi<br /> <br /> <entries> Zgadnij, jaki znacznik ................................................................................... zawiera personalia<br /> <br /> wpisu<br /> <br /> Data i treść <entry> autora blogu? ................................................................................... także mają swoje<br /> <br /> . Zbiór wpisów własne znaczniki <date>14/08/2008</date> w blogu jest ................................................................................... umieszczony <body>Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko.</body> wewnątrz ................................................................................... znacznika <entries>. </entry> <br /> <br /> ................................................................................... Każdy wpis w blogu jest </entries> ................................................................................... reprezentowany przez<br /> <br /> </blog><br /> <br /> znacznik <entry>.<br /> <br /> Nie ma<br /> <br /> niemądrych pytań<br /> <br /> P: A dlaczego nie można by przechowywać danych<br /> <br /> blogu jako zwyczajnego, niesformatowanego tekstu?<br /> <br /> O: Można by tak zrobić, jednak w takim przypadku skrypt miałby<br /> <br /> trudne zadanie z przeanalizowaniem takich danych i rozbiciem ich najpierw na poszczególne wpisy, a tych z kolei na datę i treść. XML nadaje danym znaną, przewidywalną strukturę, dzięki czemu nie ma problemów z rozpoznaniem ich poszczególnych elementów, takich jak konkretne wpisy albo data i treść wpisu, nie wspominając w ogóle o tytule czy też autorze całego blogu.<br /> <br /> P: Czy w danych blogu znacznik <entries> jest konieczny?<br /> <br /> O: Właściwie nie, jednak jego zastosowanie poprawia strukturę<br /> <br /> formatu danych i sprawia, że można ją łatwiej zrozumieć. Gdybyśmy na przykład zrezygnowali z tego znacznika w przedstawionym formacie, to trudno byłoby określić, że format ten zapewnia możliwość stosowania wielu znaczników <entry>, ale tylko po jednym znaczniku <title> i <author rel="nofollow">. Znacznik <entries> sugeruje wprost, że istnieje cała kolekcja wpisów, a to z kolei poprawia strukturę danych i stanowi dodatkową informację o tym, jak należy je przetwarzać.<br /> <br /> 556<br /> <br /> Rozdział 12.<br /> <br /> P: Co łączy XML-a z Ajaksem? O: Kiedyś krążyła opinia, że Ajax to akronim angielskich słów<br /> <br /> „Asynchronous JavaScript And XML” (asynchroniczny JavaScript i XML), co świadczy o tym, że XML jest bezpośrednio powiązany z tą techniką. Teraz jednak takie tłumaczenie uważa się za przestarzałe, gdyż XML przestał być obowiązkowym elementem techniki Ajax. Nie zmienia to jednak faktu, że XML wciąż stanowi jedną z podstaw aplikacji korzystających z techniki Ajax, gdyż stanowi doskonały mechanizm modelowania danych. Jak się przekonamy w dalszej części rozdziału, istnieje także inne powiązanie XML-a z Ajaksem — jest nim sposób obsługi techniki Ajax w języku JavaScript. JavaScript nie wymaga stosowania XML-a jako obowiązkowego formatu danych do przesyłania żądań i odpowiedzi w techniki Ajax, choć zastosowanie XML-a znacznie ułatwia obsługę przekazywanych danych. Dotyczy to niemal wszystkich danych, może za wyjątkiem tych najprostszych. A zatem, choć Ajaksowi puryści mogą twierdzić, że nie ma żadnego powiązania pomiędzy XML-em i Ajaksem, to jednak z praktycznego punktu widzenia współpraca XML-a z Ajaksem jest dosyć bliska. Zatem stary akronim wciąż jest aktualny i prawdziwy, choć może stracił nieco ze swego początkowego znaczenia. Zagadnieniem asynchroniczności Ajaksa zajmiemy się w dalszej części rozdziału.<br /> <br /> Dynamiczne dane<br /> <br /> Ciągle tego nie łapię. Dlaczego zapisywanie danych w określonym formacie sprawia, że stają się one dynamiczne?<br /> <br /> XML jako taki nie jest dynamiczny, lecz doskonale współpracuje z Ajaksem i DOM. XML to format danych, który jest najczęściej używany w aplikacjach korzystających z techniki Ajax; dlatego też jest on logicznym kandydatem do reprezentacji danych blogu, które w nowej wersji strony Reni będą przesyłane pomiędzy klientem a serwerem. To wysoce strukturalna natura XML-a sprawia, że tak dobrze nadaje się on do wymiany danych. Co więcej, podobieństwo danych XML do kodu HTML (a właściwie XHTML) sprawia, że istnieje możliwość uzyskania dostępu do danych XML za pośrednictwem DOM. W takim przypadku dane XML są widziane jako hierarchiczne drzewo węzłów. Oznacza to, że można napisać kod JavaScript, który przeanalizuje takie drzewo węzłów XML, uważnie wyselekcjonuje potrzebne dane, a następnie dynamicznie wyświetli je na stronie WWW. Właśnie to (pomijając jeszcze kilka innych czynników) sprawia, że XML jest fantastycznym sposobem przechowywania informacji, który doskonale nadaje się do wykorzystania w dynamicznych, sterowanych danymi aplikacjach internetowych.<br /> <br /> jesteś tutaj  557<br /> <br /> Dodajemy Ajaksa<br /> <br /> Ajax wzmacnia stronę MagicznaKostka Po stworzeniu fantastycznego dokumentu XML zawierającego dane całego blogu Renia jest już gotowa, by dynamicznie wczytać je na stronę MagicznaKostka, korzystając przy tym z techniki Ajax.<br /> <br /> Jak właściwie można dynamicznie wczytać dane XML na stronie WWW, używając Ajaksa?<br /> <br /> 2<br /> <br /> Serwer<br /> <br /> Serwer odbiera żądanie i zabiera się do generowania odpowiedzi.<br /> <br /> Technika Ajax jest ściśle związana z pojęciami żądania i odpowiedzi, stanowiącymi mechanizm prowadzenia komunikacji i wymiany danych pomiędzy przeglądarką i serwerem.<br /> <br /> Odpowiedź<br /> <br /> Żądaniem jest nazwa pliku XML zawierającego dane blogu Reni.<br /> <br /> blog.xml 1<br /> <br /> Przed wysłaniem żądania strona nie dysponuje danymi blogu, a co za tym idzie — nie jest w stanie wyświetlić wpisów.<br /> <br /> Przeglądarka przesyła żądanie na serwer i czeka na odpowiedź. <html> <head> ... </head> <body> ... </body> </html><br /> <br /> youcube.html<br /> <br /> 558<br /> <br /> Rozdział 12.<br /> <br /> Dynamiczne dane 3<br /> <br /> Serwer tworzy odpowiedź, umieszczając dane w pliku blogu. W odpowiedzi przesyłan a jest cała zawartość pliku XM zawierającego dane blo L gu. <blog> <title>... <author rel="nofollow">... <entries> <entry> ... </entry> ... </entries> </blog><br /> <br /> blog.xml<br /> <br /> Serwer<br /> <br /> Czasami do przetworzenia żądania i przygotowania odpowiedzi konieczny jest skrypt działający na serwerze (nie należy go myli ć ze skryptami pisanymi w języku JavaScript).<br /> <br /> Odpowiedź<br /> <br /> 4<br /> <br /> Przeglądarka rozpakowuje dane przesłane w odpowiedzi i umieszcza je na stronie WWW. Kiedy dane XML zostaną zintegrowane z kodem HTML strony WWW, przeglądarka uzyska do nich dostęp.<br /> <br /> WYTĘŻ umysł<br /> <br /> Jak sądzisz, jak wygląda kod JavaScript odpowiedzialny za utworzenie ajaksowego żądania i odebranie odpowiedzi?<br /> <br /> <html> <head> ... </head> <body> ... </body> </html><br /> <br /> youcube.html<br /> <br /> Kod JavaScript odpowiedzialny za utworzenie ajaksowego żądania i obsługę odpowiedzi działa w ramach strony WWW.<br /> <br /> jesteś tutaj  559<br /> <br /> Zakres obowiązków JavaScriptu<br /> <br /> XMLHttpRequest — JavaScript spieszy z pomocą W języku JavaScript dostępny jest wbudowany obiekt o nazwie XMLHttpRequest, stosowany w technice Ajax do generowania żądań i obsługi odpowiedzi. Obiekt ten jest stosunkowo złożony — zawiera kilka różnych metod i właściwości, które zapewniają wsparcie dla techniki Ajax.<br /> <br /> readyState Wartość liczbowa określająca stan żąda nia: 0 (niezainicjowane), 1 (otwarte), 2 (wysłane), 3 (w trakcie odbierania) i 4 (wczytane).<br /> <br /> Obiekt XMLHttpRequest udostępnia jeszcze kilka innych właściwości i metod, lecz te, które wymieniliśmy na tej stronie, są najczęściej stosowane.<br /> <br /> XMLHttpRequest<br /> <br /> status Kod statusu HTTP żądania, na przykład 404 (nie znaleziono) lub 200 (OK).<br /> <br /> readyState<br /> <br /> status onreadystatechange<br /> <br /> Te dwie właściwości wystarczają, by określić, czy ajaksowe żądanie zostało obsłużone i zwróciło ważną odpowiedź.<br /> <br /> responseText responseXML<br /> <br /> send()<br /> <br /> open()<br /> <br /> Przesyła żądanie na serwe r w celu jego przetworzeni a.<br /> <br /> responseText Dane przesłane w odpowiedzi przez serwer, zapisane jako zwyczajny łańcuch znaków.<br /> <br /> responseXML Dane przesłane w odpowiedzi przez serwer, zapisane jako obiekt zawierający drzewo węzłów XML.<br /> <br /> Rozdział 12.<br /> <br /> Metoda ta jest używana wyłącznie wtedy, gdy konieczne jest przerwanie obsługi żądania.<br /> <br /> open()<br /> <br /> send()<br /> <br /> Odwołanie do funkcji, która jest wywoływana w momencie zmiany stanu żądania.<br /> <br /> 560<br /> <br /> Przerywa żądanie.<br /> <br /> Przygotowuje żądanie, określając jego typ, docelowy adres URL oraz inne parametry.<br /> <br /> onreadystatechange<br /> <br /> Ta właściwość jest unikalna, gdyż zawiera odwołanie do niestandardowej procedury obsługi zdarzeń, która jest wywoływana za każdym razem, gdy zmieni się stan zamówienia. To właśnie w określonej tu procedurze obsługi zdarzeń przetwarzana jest odpowiedź.<br /> <br /> abort()<br /> <br /> abort()<br /> <br /> Te dwie metody współpracują ze sobą, by przygotować, a następnie przesłać żądanie na serwer.<br /> <br /> W tych dwóch właściwościach przechowywane są dane zwrócone w odpowiedzi przez serwer.<br /> <br /> Dynamiczne dane<br /> <br /> Obiekt XMLHttpRequest jest całkiem złożony Obiekt XMLHttpRequest jest niesamowicie potężny, a jednocześnie zaskakująco elastyczny. Jednak te ogromne możliwości i elastyczność sprawiają, że jest on także dosyć złożony; oznacza to, że wygenerowanie nawet najprostszego żądania przy użyciu techniki Ajax wymaga użycia stosunkowo złożonego kodu JavaScript. Po części złożoność ta jest konsekwencją różnic występujących pomiędzy przeglądarkami, jednak sytuacji wcale nie poprawia fakt, że przeróżne możliwości precyzyjnego dostrajania działania obiektów mogą przysparzać pewnych problemów, zwłaszcza gdy zależy nam wyłącznie na prostym i szybkim, dynamicznym przesłaniu danych.<br /> <br /> Obiekt XMLHttpRequest jest bardzo potężny, lecz jednocześnie kłopotliwy w użyciu.<br /> <br /> W ramach przykładu przeanalizuj poniższy kod, który jest konieczny do utworzenia obiektu XMLHttpRequest, tak by działał on prawidłowo niezależnie od używanej przeglądarki:<br /> <br /> Dla m niaków<br /> <br /> a var request = null; sposoby tworzenia obiektu tóre Problem XMLHttpRequest, gdyż niek if (window.XMLHttpRequest) { przeglądarki (Internet Explorer) z tworzeniem obiektu try { obsługują go w inny sposób. polega na tym, Request XMLHttp request = new XMLHttpRequest(); że przeglądarki same muszą } catch(e) { zapewnić jego implementację. request = null; Pocieszające jest to, że metody } i właściwości tego obiektu są // Teraz próbujemy wersji ActiveX (IE) spójnie zaimplementowane w różnych przeglądarkach; } else if (window.ActiveXObject) { innymi słowy, różnice pomiędzy try { implementacjami występują request = new ActiveXObject("Msxml2.XMLHTTP"); wyłącznie podczas jego tworzenia. // Próbujemy utworzyć starszy obiekt ActiveX (w starszych wersjach IE) } catch(e) { try { request = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { request = null; ansowany Instrukcje try-catch stanowią zaawny owa stos ów błęd ugi } mechanizm obsł cy skryptowi w języku JavaScript, pozwalają To jest nasza } reagować w razie wystąpienia Kod musi wypróbować różne<br /> <br /> }<br /> <br /> łagodnie błędów czasu wykonywania.<br /> <br /> Po utworzeniu obiektu XMLHttpRequest należy określić funkcję obsługującą żądanie, a następnie otworzyć to żądanie.<br /> <br /> niestandardowa funkcja, która zostanie wywołana, gdy serwer odpowie na żądanie.<br /> <br /> request.onreadystatechange = handler; request.open(typ, url, true); // zawsze asynchroniczne (czyli true);<br /> <br /> Podczas otwierania żądania należy określić jego typ ("GET” lub "POST”), docelowy adres URL oraz to, czy żądanie ma być asynchroniczne, czy też nie. Żądania asynchroniczne są realizowane w tle i nie zmuszają skryptów do czekania na odebranie odpowiedzi; jak zatem łatwo się domyślić, w zasadzie wszystkie żądania generowane przy użyciu techniki Ajax będą właśnie żądaniami asynchronicznymi.<br /> <br /> Otwarcie żądania powoduje, że stanie się ono gotowe do , przesłania; co więcej podczas otwierania ić żądania można określ to jego typ (czy będzie żądanie GET, czy też POST).<br /> <br /> jesteś tutaj  561<br /> <br /> Mam to<br /> <br /> Technika Ajax pozwala na<br /> <br /> Kilka słów o żądaniach GET i POST Typ żądania przesyłanego na serwer przy użyciu techniki Ajax jest bardzo ważny i odzwierciedla nie tylko to, jakie dane są przesyłane na serwer, lecz także w jakim celu żądanie zostało wygenerowane. Jednym z dostępnych typów żądań, nazywanych także metodą żądania, jest GET. Żądania tego typu służą zazwyczaj do pobierania danych z serwera, bez jednoczesnej modyfikacji jakichkolwiek zasobów zgromadzonych na serwerze. Drugim typem żądań jest POST. Żądania tego typu zazwyczaj przekazują na serwer jakieś dane, a w ich wyniku zmienia się stan zasobów serwera.<br /> <br /> przesyłanie żądań typu GET lub POST, czyli takich samych jak te, których możemy używać podczas przesyłania na serwer danych z formularzy HTML. POST<br /> <br /> GET<br /> <br /> Ten typ służy do przesyłan ia na serwer żądań zawierających dane, któ re w jakiś sposób zmien iają stan serwera, na przykład zapisują przesłane informacje w bazie danych . Serwer, w odpowiedzi na żądanie tego typu, mo że wygenerować jakieś dane. Żądania POST ide alnie nadają się do takich zadań jak dynamiczne do danie do blogu nowego wpisu na podstawie inform acji podanych przez użytkownika w formular zu na stronie WWW.<br /> <br /> Żądania tego typu są stosowane do pobierania danych z serwera, przy czym na serwerze nie zachodzą żadne zmiany. W razie konieczności na serwer można przesyłać niewielkie ilości danych, umieszczając je w adresie URL. Ten typ żądań jest optymalny do pobierania danych blogu, przechowywanych w pliku XML na serwerze.<br /> <br /> Żądanie GET<br /> <br /> Żądanie POST<br /> <br /> blog.xml Nazwa pliku XML zawierającego wszystkie dane blogu.<br /> <br /> Żądanie typu GET nie ma żadnego wpływu na serwer, gdyż jego zadaniem jest jedynie pobranie danych blogu.<br /> <br /> Date: 09/26/2008 spełniły..." Body: "Te marzenia właśnie się Image: cubeapart.png<br /> <br /> Serwer Żądanie typu POST zmienia stan serwera, gdyż do blogu zostaje dodany nowy wpis.<br /> <br /> Nowy wpis, który należy zapisać na serwerze.<br /> <br /> <blog> <title>... <author rel="nofollow">... <entries> <entry> ... </entry><br /> <br /> ... </entries> </blog><br /> <br /> Klient<br /> <br /> blog.xml<br /> <br /> Odpowiedź na żądanie GET<br /> <br /> 562<br /> <br /> Rozdział 12.<br /> <br /> Odpowiedź na żądanie POST<br /> <br /> Klient<br /> <br /> Dynamiczne dane<br /> <br /> GET czy POST? Użycie obiektu XMLHttpRequest Kiedy już zdecydujemy, jaki ma być typ żądania, i określimy go podczas otwierania żądania, będziemy mogli przesłać to żądanie na serwer celem przetworzenia. Kod służący do przesyłania żądań jest różny w zależności od tego, czy ma to być żądanie typu GET, czy POST.<br /> <br /> Podczas otwierania żądania określiliśmy, że ma to być żądanie typu GET, i podaliśmy docelowy adres URL.<br /> <br /> request.open("GET", "blog.xml", true);<br /> <br /> // zawsze asynchroniczne (czyli true)<br /> <br /> request.send(null); Żądanie jest przesyłane bez żadnych danych — to właśnie dlatego w wywołaniu metody send() przekazano wartość null.<br /> <br /> Dane blogu zapisane w pliku blog.xml na serwerze zostały pobrane przy użyciu żądania typu GET.<br /> <br /> Żądanie GET blog.xml<br /> <br /> Żądanie POST Klient<br /> <br /> Serwer<br /> <br /> Date: 09/26/2008 spełniły..." Body: "Te marzenia właśnie się Image: cubeapart.png Nowy wpis jest przesyłany na serwer w żądaniu POST.<br /> <br /> serwer Żądanie wymaga przesłania na eczne pewnych danych, dlatego też koni jest określenie ich typu.<br /> <br /> s Typ żądania POST i docelowy adre s URL (w tym przypadku jest to adre e skryptu na serwerze) są określan nia. żąda ia eran otwi zas podc<br /> <br /> request.open("POST", "addblogentry.php", true); // To zawsze są wywołania asynchroniczne request.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8); request.send("09/26/2008&Te marzenia wła...&cubeapart.png");<br /> <br /> Relax<br /> <br /> Wraz z żądaniem na serwer przesyłane są także dane przekazane w wywołaniu metody send().<br /> <br /> Nie stresuj się dobieraniem żądań typu GET lub POST. Jeśli nie masz jeszcze żadnych doświadczeń w stosowaniu metod GET lub POST (bo na przykład nie używałeś wcześniej formularzy HTML), to i tak nie masz się czym przejmować. Wraz z lekturą rozdziału, gdy zobaczysz przykład y zastosowania obu tych typów żądań, wszystko stanie się jasne i zrozum iałe.<br /> <br /> jesteś tutaj  563<br /> <br /> Rosnące problemy — upraszczanie Ajaksa<br /> <br /> Upraszczanie stosowania obiektu XMLHttpRequest Choć obiekt XMLHttpRequest zapewnia bardzo duże możliwości, to jednak opanowanie sposobów korzystania z niego jest stosunkowo kłopotliwe (choć zapewne sam to już zauważyłeś). Co gorsza, w każdej aplikacji, która chce korzystać z tego obiektu, musi się znaleźć stosunkowo rozbudowany kod mający zawsze taką samą postać. Dlatego też powstało już bardzo dużo bibliotek, które upraszczają tworzenie i stosowanie obiektu XMLHttpRequest. Wiele spośród tych bibliotek rozszerza także niektóre możliwości języka JavaScript, co jest bardzo wygodne i użyteczne, choć wymaga dodatkowej nauki.<br /> <br /> Niestandardowy<br /> <br /> Dlatego też z punktu widzenia Reni — twórczyni strony MagicznaKostka — wygodne będzie stworzenie niewielkiego własnego obiektu, który ułatwi korzystanie z obiektu XMLHttpRequest i pozwoli jej skoncentrować się wyłącznie na przesyłaniu żądań i odbieraniu odpowiedzi, a nie na zapasach z obiektem XMLHttpRequest bądź poznawaniu jakichś dodatkowych bibliotek. Ten niestandardowy obiekt o nazwie AjaxRequest będzie stanowić minimalistyczną próbę poprawienia użyteczności i ułatwienia stosowania obiektu XMLHttpRequest.<br /> <br /> przy użyciu<br /> <br /> status onreadystatechange<br /> <br /> responseText responseXML<br /> <br /> nieco ułatwia obsługę żądań przesyłanych techniki Ajax.<br /> <br /> Większość metod obiektu AjaxRequest jedynie zapewnia dostęp do właściwości obiektu XMLHttpRequest.<br /> <br /> XMLHttpRequest readyState<br /> <br /> obiekt AjaxRequest<br /> <br /> abort()<br /> <br /> AjaxRequest<br /> <br /> open()<br /> <br /> getReadyState() getStatus() getReponseText()<br /> <br /> send()<br /> <br /> getResponseXML() Standardowy obiekt XMLHttpRequest zostaje ukryty we wła ściwości request obiektu AjaxRequest .<br /> <br /> request<br /> <br /> Oprócz metody send(), którą już zaraz się zajmiemy, kolejnym elementem obiektu AjaxRequest ułatwiającym korzystanie z techniki Ajax w porównaniu z użyciem samego standardowego obiektu XMLHttpRequest jest jego konstruktor. Przedstawiony poniżej wiersz kodu to wszystko, czego potrzeba, by stworzyć obiekt AjaxRequest, potrafiący przesyłać żądania przy użyciu Ajaksa w każdej nowoczesnej przeglądarce:<br /> <br /> var ajaxReq = new AjaxRequest();<br /> <br /> 564<br /> <br /> Rozdział 12.<br /> <br /> send()<br /> <br /> Prawdziwym koniem pociągowym obiektu AjaxRequest jest metoda send(), która realizuje wszystkie czynności związane z otwieraniem i wysyłaniem żądań.<br /> <br /> Konstruktor obiektu AjaxRequest automatycznie uwzględnia i ukry wa przed nami całą złożoność tworzeni standardowego obiektu XMLHttp a Request.<br /> <br /> Dynamiczne dane<br /> <br /> Magnesiki z JavaScriptem Niestandardowy obiekt AjaxRequest stanowi „otoczkę” dla standardowego obiektu XMLHttpRequest i zapewnia znacznie prostsze sposoby wysyłania żądań przy użyciu techniki Ajax oraz późniejszej obsługi odbieranych odpowiedzi. Problem polega jednak na tym, że w kodzie metod send() obiektu AjaxRequest brakuje kilku kluczowych fragmentów. Twoim zadaniem jest uzupełnienie brakujących fragmentów kodu magnesikami zamieszczonymi u dołu strony.<br /> <br /> postDataType, postData) { AjaxRequest.prototype.send = function(type, url, handler, if (this.request != null) { // Usuwamy wcześniejsze żądanie this.request.abort(); dzi // Dodajemy pomocniczy parametr, by zapobiec pobraniu odpowie // z pamięci podręcznej przeglądarki url += "?dummy=" + new Date().getTime(); try { ; this.request.onreadystatechange = zawsze asynchroniczne (true) // true); , , this.request.open( if (type.toLowerCase() == "get") { // Generujemy żądanie GET: brak danych ); this.request.send( } else { // Generujemy żądanie POST: ostatni argument to dane this.request.setRequestHeader("Content-Type", ); this.request.send(<br /> <br /> );<br /> <br /> } } catch(e) { łowe: " + e); alert("Błąd komunikacji z serwerem.\n" + "Informacje szczegó } } }<br /> <br /> handler<br /> <br /> null<br /> <br /> url<br /> <br /> type<br /> <br /> postDataType<br /> <br /> postData<br /> <br /> jesteś tutaj  565<br /> <br /> Rozwiązanie magnetycznego ćwiczenia<br /> <br /> Magnesiki z JavaScriptem Niestandardowy obiekt AjaxRequest stanowi „otoczkę” dla standardowego obiektu XMLHttpRequest i zapewnia znacznie prostsze sposoby wysyłania żądań przy użyciu techniki Ajax oraz późniejszej obsługi odbieranych odpowiedzi. Problem polega jednak na tym, że w kodzie metod send() obiektu AjaxRequest brakuje kilku kluczowych fragmentów. Twoim zadaniem jest uzupełnienie brakujących fragmentów kodu magnesikami zamieszczonymi u dołu strony. Metoda send() przesyła żądanie na serwer, używając przy tym informacji przekazanych jako argumenty wywołania.<br /> <br /> postDataType, postData) { AjaxRequest.prototype.send = function(type, url, handler, if (this.request != null) { // Usuwamy wcześniejsze żądanie this.request.abort(); dzi // Dodajemy pomocniczy parametr, by zapobiec pobraniu odpowie kcja // z pamięci podręcznej przeglądarki Ta niestandardowa funcelu w a (); łan getTime wo Date(). wy new + ie url += "?dummy=" zostan obsłużenia odpowiedzi r. przesłanej przez serwe<br /> <br /> try { handler ; this.request.onreadystatechange = // zawsze asynchroniczne (true) true); , , e typ url pen( this.request.o if (type.toLowerCase() == "get") { Argument type metody send() // Generujemy żądanie GET: brak danych określa, czy zostanie przesłane this.request.send( żądanie typu GET, czy POST. null );<br /> <br /> Dane są przysyłane na serwer wyłącznie wtedy, gdy generowane jest żądanie typu POST.<br /> <br /> } else { // Generujemy żądanie POST: ostatni argument to dane taType.. ); postDa....... this.request.setRequestHeader("Content-Type", ....... ); this.request.send(<br /> <br /> postData<br /> <br /> } } catch(e) { łowe: " + e); alert("Błąd komunikacji z serwerem.\n" + "Informacje szczegó } } }<br /> <br /> Ten kod znajdziesz w osobnym pliku JavaScript o nazwie ajax.js; oprócz niego został tam także umieszczony kod konstruktora obiektu AjaxRequest.<br /> <br /> 566<br /> <br /> Rozdział 12.<br /> <br /> ajax.js<br /> <br /> Dynamiczne dane<br /> <br /> Aby zrozumieć ajaksowe żądania Nasz niestandardowy obiekt AjaxRequest składa się z konstruktora oraz kilku właściwości i metod, spośród których jedna jest szczególnie przydatna. Metoda send() służy do przygotowania i wysłania żądania na serwer, a wszystko to przy użyciu jednego wywołania. Metoda ta pozwala na przesyłanie żądań typu GET lub POST, co odpowiada sposobom przesyłania formularzy HTML. Różnica pomiędzy przesyłaniem żądań przy użyciu techniki Ajaks i obiektu AjaxRequest a przesyłaniem formularzy polega na tym, że w tym pierwszym przypadku nie jest konieczne przeładowywanie całej strony wyświetlonej w przeglądarce.<br /> <br /> AjaxRequest getReadyState() getStatus() getReponseText()<br /> <br /> request<br /> <br /> getResponseXML()<br /> <br /> send()<br /> <br /> send(type, url, handler, postDataType, postData)<br /> <br /> type Typ żądania — GET lub POST.<br /> <br /> handler<br /> <br /> url<br /> <br /> Adres URL serwera (w przypadku strony MagicznaKostka jest nim nazwa pliku blog.xml). W razie konieczności do tego adresu URL można dołączyć dodatkowe dane.<br /> <br /> Funkcja zwrotna służąca do obsługi żądania.<br /> <br /> postData<br /> <br /> postDataType<br /> <br /> Dane, które mają być przesłane w żąda niu (dotyczy tylko żądań typu POST, w żądaniach typu GET argument ten można pominąć). Dan e te mogą być zapisane w kilku różnych formatach.<br /> <br /> Wszystkie żądania wysyłane przy użyciu Ajaksa wymagają takich samych informacji, choć w przypadku żądań typu GET można pominąć dwa ostatnie, opcjonalne argumenty. A zatem trzy pierwsze argumenty metody send() są najważniejsze i w zupełności wystarczają, by wysyłać proste żądania. Poniżej przedstawiliśmy proste wywołanie metody send(), w którym określiliśmy wartości tylko trzech pierwszych argumentów i które generuje żądanie typu GET, by pobrać z serwera plik o nazwie filmy.xml. Typ żądania.<br /> <br /> Adres URL żądanego pliku z danymi.<br /> <br /> ajaxReq.send("GET", "filmy.xml", handleRequest); W tym przykładzie zakładamy, że obiekt AjaxRequest został już utworzony i zapisany w zmiennej ajaxReq.<br /> <br /> Typ danych wysyłanych w żądaniu (dotyczy tylko żądań typu POST, w żądaniach typu GET argument ten można pominąć).<br /> <br /> Relax<br /> <br /> Nie panikuj z powodu braku obsługi odpowiedzi na żądania.<br /> <br /> Wszelkie szczegóły związane z obsługiwaniem w kodzie JavaScript odpowiedzi na żądania generowane przy użyciu Ajaksa przedstawimy już niebawem. Na razie wystarczy, byś zapamiętał i zrozumiał, że podczas generowania żądania konieczne jest określenie własnej funkcji obsługi, która zostanie wywołana w momencie zakończenia obsługi żądania.<br /> <br /> Nasza niestandardowa funkcja, która zostanie wywołana w celu ej obsłużenia odpowiedzi nadesłan z serwera.<br /> <br /> jesteś tutaj  567<br /> <br /> Czy mogę przesłać żądanie?<br /> <br /> Żądanie Piłka po stronie serwera Wywołanie metody send() obiektu AjaxRequest powoduje przesłanie na serwer żądania. Jednocześnie po wysłaniu żądania, kiedy serwer będzie je obsługiwał, strona WWW może zająć się swoimi sprawami. To właśnie w tym momencie w pełnej krasie widać asynchroniczną naturę Ajaksa. Gdyby ajaksowe żądania były synchroniczne, cała strona musiałaby bezczynnie oczekiwać na przesłanie odpowiedzi z serwera. Jednak ze względu na to, że żądania są przesyłane asynchronicznie, strona nie jest blokowana, a wrażenia użytkowników nie doznają żadnego uszczerbku.<br /> <br /> blog.xml Gdy serwer przetwarza żądanie, strona WWW może zająć się swoimi sprawami i nie musi bezczynnie czekać na otrzymanie odpowiedzi.<br /> <br /> <html> <head> ... </head> <body> ... </body> </html><br /> <br /> youcube.html<br /> <br /> To, że strona nie jest blokowana, gdy oczekuje na odpowiedź z serwera, nie oznacza wcale, że użytkownik może w tym czasie zrobić coś produktywnego. To już zależy wyłącznie od specyfiki konkretnej strony. W przypadku strony MagicznaKostka wyświetlenie zawartości blogu jest całkowicie zależne od pomyślnego pobrania danych blogu z serwera, do czego używamy właśnie żądania przesyłanego przy użyciu Ajaksa. Dlatego też w tym przypadku wrażenia użytkownika są w całości zależne od otrzymania odpowiedzi na żądanie.<br /> <br /> Serwer<br /> <br /> Asynchroniczne żądanie generowane przy użyciu Ajaksa jest realizowane bez blokowania strony — innymi słowy, strona może funkcjonować w czasie, gdy serwer przetwarza żądanie.<br /> <br /> Kluczowe zagadnienia  <br /> <br />  <br /> <br /> 568<br /> <br /> XMLHttpRequest jest standardowym obiektem służącym do obsługi żądań w technice Ajax, jednak korzystanie z niego nie należy do najprostszych. Niestandardowy obiekt AjaxRequest jest narzędziem pozwalającym na wygodne korzystanie z techniki Ajax bez konieczności bezpośredniego posługiwania się obiektem XMLHttpRequest.<br /> <br /> Rozdział 12.<br /> <br />  <br /> <br /> Ajax pozwala na przesyłanie żądań dwóch typów: GET i POST. Wybór typu zależy od tego, czy na serwer mają być przesłane jakieś dane oraz czy dane te mają wpływ na stan serwera.<br /> <br />  <br /> <br /> Metoda send() obiektu AjaxRequest pozwala na przesyłanie żądań na serwer, przy czym można dowolnie wybrać typ żądania oraz w razie konieczności dołączyć do niego dane.<br /> <br /> Dynamiczne dane<br /> <br /> Nie ma<br /> <br /> niemądrych pytań<br /> <br /> P: Czy obiekt AjaxRequest jest<br /> <br /> chwili i niekoniecznie muszą się wiązać z przekazywaniem do przeglądarki danych HTML. Jedną z wielkich zalet Ajaksa jest to, że techniki tej można używać do pobierania danych dowolnego typu.<br /> <br /> konieczny do przesyłania żądań przy użyciu Ajaksa?<br /> <br /> O: Nie. Nic nie stoi na przeszkodzie, by<br /> <br /> korzystać wyłącznie ze standardowego obiektu XMLHttpRequest i bezpośrednio za jego pomocą generować żądania oraz obsługiwać odpowiedzi. Ale po co miałbyś tak robić, skoro jest znacznie prostszy i szybszy sposób, czyli skorzystanie z obiektu AjaxRequest? Nie ma w nim niczego magicznego, to zwyczajny obiekt, który upraszcza stosowanie techniki Ajax poprzez wykonywanie całej „brudnej roboty” związanej z przygotowywaniem i wysyłaniem żądań.<br /> <br /> P: Czy żądania i odpowiedzi<br /> <br /> w technice Ajax różnią się od zwyczajnych żądań i odpowiedzi HTTP?<br /> <br /> O: Żądania HTTP są używane przez<br /> <br /> To bardzo ważne, że Ajax może obsługiwać dane dowolnego typu, jednak równie ważnym czynnikiem jest wielkość tych danych. W przypadku żądań przesyłanych przy użyciu Ajaksa nie ograniczamy się wyłącznie do obsługi całych stron lub dokumentów. W rzeczywistości żądania te znacznie lepiej nadają się do obsługi niewielkich lub wręcz bardzo małych ilości danych. Dzięki temu Ajax pozwala na dynamiczne modyfikowanie zawartości stron WWW — wystarczy pobrać dane z serwera, a następnie umieścić je na stronie. Co najlepsze, wszystkie takie zmiany w ogóle nie wymagają przeładowywania strony w przeglądarce.<br /> <br /> P: A zatem Ajax pozwala<br /> <br /> dynamicznie składać strony WWW z wielu kawałków?<br /> <br /> przeglądarki WWW do pobierania z serwerów całych stron WWW. Żądania i odpowiedzi generowane w technice Ajax są podobne do żądań i odpowiedzi HTTP, jednak różnią się od nich pod kilkoma ważnymi względami. Otóż mogą one być generowane w dowolnej<br /> <br /> O: Tak! Takie jest podstawowe założenie<br /> <br /> tej techniki. Jednak tworzenie stron z wielu fragmentów to jeszcze nie wszystko. Bardzo ważny jest także czas, w którym informacje są pobierane i wyświetlane.<br /> <br /> <br /> <br /> ?<br /> <br /> Żądania Ajaksa są generowane i obsługiwane w czasie rzeczywistym, przy czym często nie przeszkadzają w korzystaniu ze strony. Innymi słowy, użytkownicy nie są blokowani i nie muszą czekać na przeładowanie całej strony, kiedy jedyną rzeczą, jaką chcieli zrobić, jest aktualizacja niewielkiego fragmentu jej treści. Taka sekcja może być pobierana „w tle”, a w tym samym czasie użytkownik może czytać pozostałe fragmenty strony i korzystać z nich.<br /> <br /> P: A co wspólnego mają z tym<br /> <br /> wszystkim typy żądań — GET i POST?<br /> <br /> O: Typy te określają sposób, w jaki<br /> <br /> żądania będą obsługiwane na serwerze. Trzeba zauważyć, że nie różnią się one od siebie pod względem możliwości żądania pobrania z serwera dowolnych danych w dowolnej chwili. Innymi słowy, oba typy żądań zapewniają te same korzyści, jakich dostarcza nam cała technika Ajax. Podstawowe rozróżnienie pomiędzy typami GET i POST jest związane z tym, czy w wyniku obsługi żądania i przesłanych w nim danych zmienia się stan serwera (na przykład poprzez zapisanie przesłanych informacji w bazie danych). Jeśli stan serwera zmienia się, to mamy do czynienia z żądaniem POST, w przeciwnym razie — z żądaniem GET.<br /> <br /> <br /> <br /> Jakie jest moje przeznaczenie? Dopasuj poszczególne elementy związane z techniką Ajax do opisów ich przeznaczenia. XMLHttpRequest<br /> <br /> Pobiera dane bez wprowadzania jakichkolwiek modyfikacji na serwerze.<br /> <br /> GET<br /> <br /> Używając techniki Ajax, przesyła na serwer żądanie, na które serwer odpowie.<br /> <br /> send()<br /> <br /> Przesyła na serwer dane, które w jakiś sposób zmienią jego stan.<br /> <br /> AjaxRequest<br /> <br /> Standardowy obiekt JavaScript, umożliwiający korzystanie z techniki Ajax.<br /> <br /> POST<br /> <br /> Niestandardowy obiekt, który ma nam pomóc w obsłudze żądań i odpowiedzi przesyłanych przy użyciu Ajaksa.<br /> <br /> jesteś tutaj  569<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> ?<br /> <br /> <br /> <br /> <br /> <br /> Jakie jest moje przeznaczenie? Dopasuj poszczególne elementy związane z technologią Ajax do opisów ich przeznaczenia.<br /> <br /> XMLHttpRequest<br /> <br /> Pobiera dane bez wprowadzania jakichkolwiek modyfikacji na serwerze.<br /> <br /> GET<br /> <br /> Używając techniki Ajax, przesyła na serwer żądanie, na które serwer odpowie.<br /> <br /> send()<br /> <br /> Przesyła na serwer dane, które w jakiś sposób zmienią jego stan.<br /> <br /> AjaxRequest<br /> <br /> Standardowy obiekt JavaScript, umożliwiający korzystanie z techniki Ajax.<br /> <br /> POST<br /> <br /> Niestandardowy obiekt, który ma nam pomóc w obsłudze żądań i odpowiedzi przesyłanych przy użyciu Ajaksa.<br /> <br /> AjaxRequest t<br /> <br /> XMLHttpReques<br /> <br /> Napisany przez nas obiekt AjaxRequest służy jako „otoczka” standardowego obiektu XMLHttpRequest i ułatwia korzystanie z techniki Ajax.<br /> <br /> GET blog.xml Metoda send() ko z Ajaks a, by w rzysta ysłać ż typu G ądanie ET lub POST.<br /> <br /> Serwer<br /> <br /> Date: 09/26/2008 spełniły..." Body: "Te marzenia właśnie się g Image: cubeapart.pn<br /> <br /> POST<br /> <br /> 570<br /> <br /> Rozdział 12.<br /> <br /> send()<br /> <br /> Dynamiczne dane<br /> <br /> Interaktywne strony zaczynają się od obiektu żądania Niezależnie od tego, w jaki sposób mamy zamiar korzystać z Ajaksa, ani od tego, jakie dane mamy zamiar za jego pomocą pobierać, wszelka komunikacja realizowana przy użyciu tej techniki rozpoczyna się od żądania. A zatem pierwszym zadaniem na drodze do przekształcenia swojego blogu w dynamiczną, sterowaną danymi aplikację internetową, jakie Renia musi wykonać, jest zastosowanie Ajaksa do pobrania z serwera pliku XML zawierającego dane blogu.<br /> <br /> Wygląda na to, że muszę utworzyć obiekt AjaxRequest, a następnie użyć go do przesłania żądania dotyczącego pliku z danymi mojego blogu.<br /> <br /> 1<br /> <br /> Utworzyć obiekt AjaxRequest.<br /> <br /> 2<br /> <br /> Wykonać żądanie typu GET, które pobierze z serwera plik blog.xml.<br /> <br /> 3<br /> <br /> Obsłużyć żądanie…? co należy zrobić Renia wciąż nie za bardzo wie, może się razie na ak jedn u; krok 3. ch w rama h. dwóc ch wszy pier na ć rowa skoncent<br /> <br /> Zaostrz ołówek Napisz kod, który utworzy obiekt AjaxRequest, a następnie użyje go do wygenerowania żądania GET i pobrania z serwera pliku XML zawierającego dane blogu Reni. .................................................................................................................................................................... ....................................................................................................................................................................<br /> <br /> jesteś tutaj  571<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie Tworzone żądanie jest żądaniem typu GET, gdyż jedyną rzeczą, jaką chcemy zrobić, jest pobranie z serwera pliku z danymi blogu.<br /> <br /> 1<br /> <br /> Napisz kod, który utworzy obiekt AjaxRequest, a następnie użyje go do wygenerowania żądania GET i pobrania z serwera pliku XML zawierającego dane blogu Reni.<br /> <br /> var ajaxReq = new AjaxRequest();<br /> <br /> ....................................................................................................................................................<br /> <br /> 2 .................................................................................................................................................... ajaxReq("GET", "blog.xml", handleRequest); Jako adres URL żądania podajemy nazwę pliku.<br /> <br /> Żaden z poprzednich argumentów nie nie ma wielkiego znaczenia, jeśli zi obsłużymy odpowiednio odpowied w funkcji handleRequest().<br /> <br /> Zawołaj mnie, kiedy skończysz<br /> <br /> Skrypt na stronie obsługuje odpowiedzi na żądania przy użyciu niestandardowej funkcji zwrotnej.<br /> <br /> Po przesłaniu żądania przy wykorzystaniu Ajaksa rola przeglądarki ulega zmianie — teraz to ona czeka na odpowiedź z serwera. Jednak ponieważ żądania wykonywane przy użyciu Ajaksa są zazwyczaj żądaniami asynchronicznymi, użytkownik może bez przeszkód korzystać ze strony, podczas gdy przeglądarka w niezauważalny sposób oczekuje na otrzymanie odpowiedzi. Innymi słowy, fakt przesłania żądania i oczekiwanie na odpowiedź serwera nie blokują działania strony. Kiedy serwer zakończy przetwarzanie żądania, przesłana przez niego odpowiedź jest przetwarzana w kodzie JavaScript przez funkcję zwrotną, nazywaną także funkcją obsługi żądania.<br /> <br /> <blog> <title>... <author rel="nofollow">... <entries> <entry> ... </entry><br /> <br /> łana z serwera Odpowiedź jest przesy następnie jest do przeglądarki, gdzie kcję zwrotną. obsługiwana przez fun<br /> <br /> ... </entries> </blog><br /> <br /> blog.xml<br /> <br /> Klient przy użyciu Ajaksa // Obsługa żądań przesyłanych { function handleRequest() { 4 && ajaxReq.getStatus() == 200) if (ajaxReq.getReadyState() == w odpowiedzi // Zapisujemy dane XML przesłane g")[0]; ML().getElementsByTagName("blo var xmlData = ajaxReq.getResponseX<br /> <br /> Serwer<br /> <br /> dla całego blogu // Odczytujemy i ustawiamy podpis agName("author")[0]); " + getText(xmlData.getElementsByT Blog.prototype.signature = "by (reprezentujących wpisy) Blog obiektów // Tworzymy tablicę yTagName("entry"); tElementsB xmlData.ge = entries var ngth; i++) { for (var i = 0; i < entries.le // Tworzymy wpis (obiekt Blog) ")[0]), [i].getElementsByTagName("body blog.push(new Blog(getText(entries mentsByTagName("date")[0])), new Date(getText(entries[i].getEle ByTagName("image")[0]))); getText(entries[i].getElements<br /> <br /> Przeglądarka<br /> <br /> } blogu // Aktywujemy przyciski do obsługi h").disabled = false; document.getElementById("searc ll").disabled = false; document.getElementById("showa andom").disabled = false; document.getElementById("viewr<br /> <br /> 1<br /> <br /> Utworzyć obiekt AjaxRequest.<br /> <br /> 2<br /> <br /> Wykonać żądanie typu GET, które pobierze z serwera plik blog.xml.<br /> <br /> 3<br /> <br /> Obsłużyć żądanie.<br /> <br /> 572<br /> <br /> Rozdział 12.<br /> <br /> // Wyświetlamy blog showBlog(5); }<br /> <br /> Funkcja zwrotna handleRequest() jest całkowicie niestandardowa — musi być umieszczona na stronie przez programistę tworzącego kod JavaScript aplikacji.<br /> <br /> }<br /> <br /> handleRequest();<br /> <br /> Dynamiczne dane<br /> <br /> Obsługa żądania… bezproblemowa Niestandardowa funkcja zwrotna obsługująca żądania, która w przypadku strony MagicznaKostka nosi nazwę handleRequest(), jest wywoływana w momencie, gdy przeglądarka odbierze odpowiedź nadesłaną przez serwer. Oprócz informacji, że żądanie zostało pomyślnie obsłużone, zadaniem tej funkcji jest także zrobienie czegoś pożytecznego z danymi nadesłanymi w odpowiedzi.<br /> <br /> Rozumiem, że funkcja obsługi żądania jest wywoływana, by zrobić coś sensownego z odpowiedzią, ale niby w jaki sposób można uzyskać dostęp do danych przesłanych w tej odpowiedzi?<br /> <br /> Metody obiektu AjaxRequest zapewniają dostęp do danych przesłanych w odpowiedzi. Funkcja zwrotna obsługująca żądania ma dostęp do danych odpowiedzi dzięki dwóm metodom obiektu AjaxRequest: getResponseText() oraz getResponseXML().<br /> <br /> AjaxRequest getReadyState() getStatus() getReponseText()<br /> <br /> request<br /> <br /> getResponseText() Pobiera dane z odpowiedzi w formie zwyczajnego tekstu.<br /> <br /> getResponseXML()<br /> <br /> send()<br /> <br /> getResponseXML() Pobiera dane z odpowiedzi jako strukturalny kod XML.<br /> <br /> Tylko jedna z tych metod ma dostęp do ważnych danych w konkretnej odpowiedzi, co oznacza, że to format przesłanych danych determinuje, której z tych metod należy użyć. Jak można się domyślić, jeśli w odpowiedzi zostały przesłane dane XML, to należy skorzystać z metody getResponseXML(), a to jednocześnie oznacza, że metoda getResponseText() nie zwróci żadnych sensownych danych. Analogicznie dzieje się w odwrotnej sytuacji, czyli gdy w odpowiedzi został przesłany zwyczajny tekst, a nie dane XML.<br /> <br /> WYTĘŻ umysł<br /> <br /> w jaki Skoro wiemy, że dane XML mają taką samą strukturę jak kod HTML, to jak sądzisz, żądania? obsługi funkcji kodzie w sposób można uzyskać dostęp do danych XML<br /> <br /> jesteś tutaj  573<br /> <br /> Sytuację wyjaśnia DOM<br /> <br /> Skoro dane XML to zbiór znaczników, to czy przez przypadek nie moglibyśmy z nich korzystać, używając DOM?<br /> <br /> DOM spieszy z pomocą Talent Reni do rozwiązywania układanek niewątpliwie się tutaj przydał, gdyż jej pomysł z użyciem DOM do przetwarzania danych XML przesłanych w odpowiedzi jest wyjątkowo dobry. DOM służy do manipulowania kodem HTML przedstawionym w postaci drzewa węzłów. Jednak DOM nie ma nic wspólnego z samym HTML-em, a to oznacza, że można go używać także do operowania na kodzie XML przedstawionym jako drzewo węzłów. Wystarczy zatem, że Renia zacznie sobie wyobrażać dane swojego blogu jako hierarchiczną grupę węzłów. ndows-1250"?> <?xml version="1.0" encoding="wi <blog> nek miłośników przestrzennych układa blog tka znaKos >Magic <author rel="nofollow">Renia - miłośniczka układa <entries> <entry> <date>08/14/2008</date> iwe cudeńko.</body> zamówioną układankę. To prawdz <body>Dostałam właśnie nową,<br /> <br /> </entry> ... <entry> <date>09/26/2008</date> rozpada się na ejsze... Teraz widzę, że kostka <body>Sny stają się coraz dziwni > znaczy?</body> części. Co <strong>to</strong <image>cubeapart.png</image> </entry> </entries> </blog><br /> <br /> body<br /> <br /> "... Co"<br /> <br /> strong<br /> <br /> "znaczy?"<br /> <br /> author<br /> <br /> "Renia – miłośniczka układanek"<br /> <br /> Znacznik <author rel="nofollow"> zawiera nazwę autora zapisaną w potomnym węźle tekstowym.<br /> <br /> 574<br /> <br /> Rozdział 12.<br /> <br /> Znacznik <body> może zawierać żnie kilka elementów potomnych, zale są u wpis ci treś w czy , od tego umieszczone jakieś formatujące znaczniki HTML, czy nie.<br /> <br /> "to"<br /> <br /> Renia musi mieć możliwość pobrania zawartości z węzłów reprezentujących dane XML — można się domyślić, że jest to czynność, którą często będziemy powtarzać, zatem jest też dobrą kandydatką na funkcję. Przecież nie ma najmniejszego sensu, by kilkakrotnie umieszczać na stronie ten sam kod JavaScript, jeśli tylko można tego uniknąć.<br /> <br /> Dynamiczne dane<br /> <br /> Funkcja getText() pod lupą Niestandardowa funkcja getText() obsługuje żmudne zadania przeglądania elementów (węzłów) w drzewie DOM i pobierania ich zawartości. Argumen<br /> <br /> t elem określa function getText(elem) { element, którego należy pobrać.<br /> <br /> var text = "";<br /> <br /> zawartość W pętli przeglądamy wszystkie węzły potomne wskazanego elementu.<br /> <br /> if (elem) { if (elem.childNodes) { for (var i = 0; i < elem.childNodes.length; i++) { var child = elem.childNodes[i]; if (child.nodeValue) text += child.nodeValue; else {<br /> <br /> Dodajemy zawartość węzła potomnego do zmiennej text.<br /> <br /> if (child.childNodes[0]) if (child.childNodes[0].nodeValue) text += child.childNodes[0].nodeValue; } } } } return text; <br /> <br /> Zwracamy wartość zmiennej text, w której jest zapisana cała zawartość z węzłów potomnych wskazanego elementu.<br /> <br /> Jeśli węzeł potomny zawiera kolejne węzły potomne, to pobieramy zawartość tekstową z pierwszego z nich i przechodzimy do dalszej analizy.<br /> <br /> }<br /> <br /> Zaostrz ołówek Zakładając, że dane XML przekazane w odpowiedzi na żądanie zostały już zapisane w zmiennej xmlData, napisz kod, który określi podpis autorki blogu — ma nim być zawartość znacznika XML o nazwie <author rel="nofollow">. ....................................................................................................................................................................<br /> <br /> jesteś tutaj  575<br /> <br /> Odpowiedź na żądanie bez tajemnic<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Zakładając, że dane XML przekazane w odpowiedzi na żądanie zostały już zapisane w zmiennej xmlData, napisz kod, który określi podpis autorki blogu — ma nim być zawartość znacznika XML o nazwie <author rel="nofollow">.<br /> <br /> Blog.prototype.signature = "napisała: " + getText(xmlData.getElementsByTagName("author")[0]);<br /> <br /> .............................................................................................................................................................................. Aby określić wartość właściwości klasowej signature, musimy odwołać się do niej za pośrednictwem obiektu prototyp e.<br /> <br /> Do pobrania tekstowej zawartości znacznika <author rel="nofollow"> należy wykorzystać funkcję pomocniczą getText().<br /> <br /> W dokumencie XML powinien być dostępny tylko jeden element <author rel="nofollow">, zatem korzystamy z pierwszego elementu zwróconego przez wywołanie metody getElementsByTagName().<br /> <br /> Obsługa odpowiedzi w technice Ajax Temat dzisiejszego wywiadu:<br /> <br /> Wyznania handleRequest() — funkcji obsługi żądania Head First: Słyszeliśmy, że całkiem dobrze idzie ci<br /> <br /> handleRequest(): Hm… W zasadzie to już nie zależy ode<br /> <br /> handleRequest(): Kiedy do przeglądarki dotrze odpowiedź na żądanie wygenerowane przy użyciu Ajaksa, zostaję wywołana, by ją obsłużyć; niejednokrotnie wiąże się to z wykonaniem jakichś operacji na danych przesłanych w odpowiedzi. W pierwszej kolejności mam za zadanie sprawdzić, czy żądanie zostało poprawnie obsłużone na serwerze. Jeśli na serwerze wszystko poszło dobrze, to kolejnym moim zadaniem jest pobranie danych z odpowiedzi i zadbanie o ich właściwą integrację ze stroną wyświetloną w przeglądarce.<br /> <br /> Head First: A dlaczego?<br /> <br /> odpowiadanie na żądania przesyłane przy użyciu techniki Ajax. Powiedz, z czym to się wiąże?<br /> <br /> Head First: Czyli oznacza to, że jesteś wywoływana, gdy żądanie zostanie zakończone?<br /> <br /> handleRequest(): Owszem. W rzeczywistości podczas<br /> <br /> całego procesu obsługi żądania jestem wywoływana nawet kilka razy, jednak programiści zazwyczaj chcą, bym zrobiła coś konkretnego wyłącznie na samym końcu obsługi żądania.<br /> <br /> Head First: No dobrze, a skąd wiesz, kiedy to będzie? handleRequest(): Cóż… obiekt AjaxRequest udostępnia kilka metod, których mogę użyć, by sprawdzić stan oraz status żądania i upewnić się, czy zostało ono zakończone bez żadnych problemów. Head First: A kiedy to się już stanie, to skąd wiesz, co masz robić?<br /> <br /> 576<br /> <br /> Rozdział 12.<br /> <br /> mnie. Pamiętaj, że jestem funkcją niestandardową, a zatem zmieniam się w każdej aplikacji.<br /> <br /> handleRequest(): Ponieważ każda aplikacja używa danych przesyłanych z serwera w inny sposób, to, jak zostaną one wykorzystane w całości, zależy od danej aplikacji. Dlatego ja też muszę się zmieniać. Head First: Chwileczkę, czy chcesz przez to powiedzieć, że ktoś musi cię pisać od nowa dla każdej aplikacji?<br /> <br /> handleRequest(): Właśnie tak. Tylko takie rozwiązanie<br /> <br /> jest sensowne, gdyż internetowy sklep z koszykiem na zakupy będzie przetwarzać odbierane odpowiedzi zupełnie inaczej niż na przykład prosty blog. Ajax zapewnia, że zostanę wywołana zawsze, gdy serwer skończy obsługiwać żądanie, lecz od tego momentu wszystko, co się stanie, zależy wyłącznie od aplikacji.<br /> <br /> Head First: A zatem jednym z zadań, jakie należy wykonać podczas tworzenia aplikacji korzystających z technologii Ajax, jest stworzenie funkcji obsługi żądań?<br /> <br /> handleRequest(): Właśnie tak. To w nich skupia się najwięcej pracy, jaką wykonują aplikacje korzystające z Ajaksa.<br /> <br /> Head First: To naprawdę bardzo ciekawe. Dziękujemy. handleRequest(): Zawsze chętnie odpowiadam na żądanie.<br /> <br /> Dynamiczne dane<br /> <br /> Bądź komentatorem JavaScriptu Twoim zadaniem jest wcielić się w rolę komentatora JavaScriptu i dodać jak najwięcej komentarzy, które wyjaśnią, co się właściwie dzieje w funkcji handleRequest(). Zapamiętaj, że magiczną liczbą jest 7. Czy znajdziesz 7 rzeczy zapewniających udaną obsługę żądania?<br /> <br /> function handleRequest() { if (ajaxReq.getReadyState() == 4 && ajaxReq.getStatus() == 200) { // Zapisujemy dane XML przesłane w odpowiedzi var xmlData = ajaxReq.getResponseXML().getElementsByTagName("blog")[0]; // Odczytujemy i ustawiamy podpis dla całego blogu Blog.prototype.signature = "by " + getText(xmlData.getElementsByTagName("author")[0]); // Tworzymy tablicę obiektów Blog (reprezentujących wpisy) var entries = xmlData.getElementsByTagName("entry"); for (var i = 0; i < entries.length; i++) { // Tworzymy wpis (obiekt Blog) blog.push(new Blog(getText(entries[i].getElementsByTagName("body")[0]), new Date(getText(entries[i].getElementsByTagName("date")[0])), getText(entries[i].getElementsByTagName("image")[0]))); } // Wyświetlamy blog showBlog(5); } }<br /> <br /> jesteś tutaj  577<br /> <br /> Bądź rozwiązaniem<br /> <br /> Bądź komentatorem JavaScriptu Twoim zadaniem jest wcielić się w rolę komentatora JavaScriptu i dodać jak najwięcej komentarzy, które wyjaśnią, co się właściwie dzieje w funkcji handleRequest(). Zapamiętaj, że magiczną liczbą jest 7. Czy znajdziesz 7 rzeczy zapewniających udaną obsługę żądania? się W pliku XML z danymi znajduje m tylko jeden znacznik <blog>, zate cy pobieramy pierwszy element tabli dy zwróconej przez wywołanie meto getElementsByTagName().<br /> <br /> Upewniamy się, że żądanie zostało pomyślnie obsłużone — w tym celu sprawdzamy jego stan i status.<br /> <br /> Określamy podpis autorki blogu na podstawie zawartości znacznika <author rel="nofollow">.<br /> <br /> function handleRequest() { if (ajaxReq.getReadyState() == 4 && ajaxReq.getStatus() == 200) { // Zapisujemy dane XML przesłane w odpowiedzi var xmlData = ajaxReq.getResponseXML().getElementsByTagName("blog")[0]; // Odczytujemy i ustawiamy podpis dla całego blogu Blog.prototype.signature = "by " + getText(xmlData.getElementsByTagName("author")[0]); Pobieramy wszystkie elementy o nazwie<br /> <br /> // Tworzymy tablicę obiektów Blog (reprezentujących wpisy) entry, bo to w nich umieszczone są poszczególne wpisy w blogu. Nie musimy var entries = xmlData.getElementsByTagName("entry"); pobierać elementu o nazwie entries, gdyż for (var i = 0; i < entries.length; i++) { możemy bezpośrednio pobrać elementy entry. // Tworzymy wpis (obiekt Blog) blog.push(new Blog(getText(entries[i].getElementsByTagName("body")[0]), new Date(getText(entries[i].getElementsByTagName("date")[0])), getText(entries[i].getElementsByTagName("image")[0]))); } ekt Blog Tu tworzymy nowy obi wpis tny kre kon cy ują reprezent licy tab iec kon na go i dodajemy tym celu wpisów, wywołując w Array. metodę push() obiektu<br /> <br /> // Wyświetlamy blog showBlog(5); } } Wywołujemy funkcję showBlog(), by wyświetlić na stronie pięć ostatnich wpisów.<br /> <br /> 578<br /> <br /> Rozdział 12.<br /> <br /> 1<br /> <br /> Utworzyć obiekt AjaxRequest.<br /> <br /> 2<br /> <br /> Wykonać żądanie typu GET, które pobierze z serwera plik blog.xml.<br /> <br /> 3<br /> <br /> Obsłużyć żądanie.<br /> <br /> Gotowe!<br /> <br /> Dynamiczne dane<br /> <br /> Teraz strona MagicznaKostka jest w pełni zależna od swoich danych Renia jest podekscytowana zmianami, jakie dzięki technologii Ajax mogła wprowadzić w swoim blogu (pozwalają jej zaoszczędzić masę czasu), ma jednak duży problem związany z korzystaniem ze strony podczas wczytywania danych blogu.<br /> <br /> Najnowszą wersję kodu strony Mag możesz pobrać z serwera FTP wydaicznaKostka Helion; znajdziesz ją pod adresem wnictwa ftp://ftp.helion.pl/przyklady/hfjsc. zip.<br /> <br /> Najnowsza wersja blogu, z danymi umieszczonymi w odrębnym pliku XML, działa naprawdę rewelacyjnie. Ale czy jest jakiś sposób, by pokazać użytkownikowi, że strona właśnie wczytuje dane z serwera? To może być cokolwiek — byleby pokazać użytkownikowi, że strona jest zajęta.<br /> <br /> <?xml version="1.0" encoding="windows-1250"?> <blog> <title>MagicznaKostka - blog miłośników przestrzennych układanek Renia - miłośniczka układanek 08/14/2008 Dostałam właśnie nową, zamówioną układankę. To prawdziwe cudeńko. 08/19/2008 Udało mi się już ułożyć tę nową układankę. Już mnie znudziła, więc szukam czegoś nowego. 08/16/2008 Już mnie głowa rozbolała od zabaw tą nową kostką. Muszę sobie odpocząć. 08/21/2008 Znalazłam w internecie układankę 7x7x7. O rany! Zabawa będzie super. 08/29/2008 Spotkałam się z kilkoma innymi zapaleńcami, by porozmawiać o układaniu kostki 7x7x7. I szczerze mówiąc, mam mieszane uczucia. 08/01/2008 Zdecydowałam się zamówić tę przerażającą kostkę 7x7x7. Już zaczynam się duchowo przygotowywać na jej układanie. 09/3/2008 Brałam udział w wyścigu przed lokalnym sklepem z zabawkami, który przestał sprzedawać układanki. Miłośnicy układanek — moc jest z nami! 08/5/2008 Dostałam zamówioną kostkę 7x7x7 — przez jakiś czas pewnie będę milczeć. 09/19/2008 O rany... zajęło mi to prawie dwa tygodnie, ale w końcu udało mi się ułożyć tę kostkę! cube777.png 09/24/008 Śniło mi się dziś w nocy, że goni mnie ogromna kostka... Brrrr! 09/26/2008 Sny stają się coraz dziwniejsze... Teraz widzę, że kostka rozpada się na części. Co to znaczy? cubeapart.png

Teraz postać blogu w całości zależy od jego danych…

Jednak niektórzy użytkownicy mogą być zaskoczeni tym, że podczas pobierania danych strona jest zupełnie pusta.

Zaostrz ołówek Uzupełnij brakujący wiersz kodu funkcji loadBlog(), który w momencie pobierania danych z serwera wyświetli na stronie obrazek „oczekiwania”, zapisany w pliku wait.gif. Podpowiedź: skorzystaj z głównego elementu div o identyfikatorze ”blog”. function loadBlog() { ....................................................................................... ajaxRequest.send("GET", "blog.xml", handleRequest); }

jesteś tutaj  579

Rozwiązanie ćwiczenia

Zaostrz ołówek Rozwiązanie

Uzupełnij brakujący wiersz kodu funkcji loadBlog(), który w momencie pobierania danych z serwera wyświetli na stronie obrazek „oczekiwania”, zapisany w pliku wait.gif. Podpowiedź: skorzystaj z głównego elementu div o identyfikatorze "blog".

function loadBlog() { document.getElementById("blog").innerHTML = "Wczytuję..."; ....................................................................................... ajaxRequest.send("GET", "blog.xml", handleRequest); }

Podczas wczytywania danych blogu z serwera obrazek „oczekiwania” całkowicie zastępuje treści wpisów prezentowane na stronie.

W tym przypadku, kiedy dodajemy znacznik img zawierający dwa atry buty, zastosowanie właściwości innerHTM znacznie prostsze niż korzystanie L jest z DOM.

Animowany obrazek „oczekiwania” jest wyświetlany na stronie zamiast zawartości blogu, by poinformować użytkownika o tym, że strona właśnie wczytuje dane.

wait.gif

Nie ma

niemądrych pytań

P: Ostatni wpis w blogu zawiera znacznik HTML

P: W jaki sposób działa stan gotowości oraz status

O: Pamiętaj, że kod XML może reprezentować dowolne dane.

O: Wartości tych dwóch właściwości są pobierane z obiektu

. Jak to możliwe, skoro plik zawiera dane XML?

W tym przypadku, skoro wiemy, że treść wpisu jest wyświetlana na stronie WWW, nic nie stoi na przeszkodzie (przynajmniej z technicznego punktu widzenia), abyśmy umieścili w niej znaczniki HTML, które określą jej wygląd. Innymi słowy, treść konkretnego wpisu może zawierać znaczniki HTML, które wraz z nią zostaną umieszczone w drzewie węzłów danych XML jako specjalne znaczniki formatujące. Rozwiązanie to jest jednak ryzykowne i kłopotliwe, gdyż zmusza nas do odtworzenia węzłów formatujących w kodzie HTML, który jest przygotowywany na podstawie danych XML, a następnie umieszczany na stronie. W skryptach używanych na stronie MagicznaKostka stosujemy aktualnie prostsze rozwiązanie, w którym zamiast pełnej analizy drzewa DOM pobieramy jedynie zawartość węzłów tekstowych, a wszelkie inne węzły ignorujemy. A zatem Renia może dodawać formatujące znaczniki HTML do treści wpisów, lecz jak na razie nie są one w żaden sposób uwzględniane. Może w przyszłej wersji strony to się zmieni. Najważniejsze jest to, że na stronie wyświetlany jest tekst zawierający treść wpisów.

580

Rozdział 12.

odpowiedzi?

XMLHttpRequest. Pierwsza z nich określa stan żądania, na przykład niezainicjowane (wartość 0) lub wczytane (wartość 4). Z kolei druga właściwość określa status żądania, taki jak 404 (nie znaleziono) lub 200 (OK). Oczywiście można dokładniej sprawdzać te właściwości, jednak nie jest to konieczne. W zupełności wystarczy, jeśli zapamiętasz, że żądanie przesyłane przy użyciu Ajaksa można uznać za zakończone pomyślnie, jeśli jego stan ma wartość 4 (wczytane), a status wartość 200 (OK). To właśnie z tego powodu funkcja handleRequest() przystępuje do działania wyłącznie wtedy, gdy oba te warunki są spełnione.

Dynamiczne dane

Niedziałające przyciski Choć przemiana, jaką przeszła strona blogu Reni, jest w znaczniej mierze niewidoczna dla jej użytkowników, to jednak na pewno zauważą oni pewien poważny problem związany z działaniem jej interfejsu. Konkretnie rzecz biorąc, wygląda na to, że przyciski umieszczone na stronie nie działają tak, jak powinny.

Użytkownicy sygnalizują, że czasami przyciski nie działają tak, jak powinny. Po kliknięciu przycisku nic się nie dzieje. Co więcej, czasami po kliknięciu przycisku znika treść blogu. Co się dzieje?

Z jakiegoś powodu przyciski czasami nie działają, a gdy tak się dzieje, treść blogu staje się niewidoczna.

Zepsute przyciski = niezadowoleni użytkownicy

WYTĘŻ umysł

Dlaczego przyciski na stronie blogu nie działają? Jak sądzisz, w którym momencie procesu wczytywania strony występuje problem z przyciskami?

Renia jeszcze jest spo ale musi szybko zrozumkojna, ieć przyczynę problemu.

jesteś tutaj  581

Kiedy mogę ich używać?

Przyciski potrzebują danych Problem z przyciskami na stronie MagicznaKostka polega na tym, że mogą one działać dopiero wtedy, gdy zostaną wczytane dane blogu. A ponieważ dane blogu są aktualnie wczytywane z zewnętrznego pliku XML, zatem przez pewien czas, na szczęście zazwyczaj bardzo krótki, dane blogu nie będą dostępne. W tym czasie wyświetlanie przycisków na stronie nie ma większego sensu, gdyż i tak nie działają, a jedynie wprowadzają użytkowników w błąd.

A czy nie można by po prostu dezaktywować przycisków, kiedy dane nie są dostępne?

Dezaktywacja przycisków jest doskonałym pomysłem. Dezaktywacja przycisków na czas wczytywania danych blogu jest prostym i eleganckim sposobem rozwiązania problemu z ich działaniem. Ponieważ żądanie pobierające dane blogu jest generowane bezpośrednio po wczytaniu strony, zatem początkowo przyciski mogą być nieaktywne, a ich uaktywnienie może nastąpić wewnątrz funkcji handleRequest(), czyli wtedy, gdy będziemy już wiedzieli, że obsługa żądania została zakończona. Aby dezaktywować przyciski, musimy skorzystać z atrybutu disabled znacznika . Wystarczy umieścić ten atrybut w znaczniku w kodzie HTML strony, a przycisk stanie się nieaktywny. Aby uaktywnić przycisk, wystarczy w kodzie JavaScript przypisać wartość false właściwości disabled elementu reprezentującego przyciski.



buttonElem.disabled = false;

582

Rozdział 12.

Dynamiczne dane

Magnesiki z JavaScriptem Korzystając z magnesików z kodem, uzupełnij kod strony MagicznaKostka, tak by umieszczone na niej przyciski były nieaktywne aż do momentu wczytania danych. Niektórych magnesików będziesz musiał użyć więcej niż jeden raz.

k MagicznaKostka - blog miłośników przestrzennych układane <script type="text/javascript" src="ajax.js"> </script> <script type="text/javascript" src="date.js"> </script> <script type="text/javascript"> ... // Obsługa żądań przesyłanych przy użyciu Ajaksa function handleRequest() { 200) { if (ajaxReq.getReadyState() == 4 && ajaxReq.getStatus() == ... // Aktywujemy przyciski do obsługi blogu ). document.getElementById( ). document.getElementById( ). document.getElementById(<br /> <br /> = = =<br /> <br /> ; ; ;<br /> <br /> ... } } ... </script> </head> <body onload="loadBlog();"> k</h3> <h3>MagicznaKostka - blog miłośników przestrzennych układane /> <img src="cube.png" alt="MagicznaKostka" <input type="button" id="search" value="Przeszukaj blog" onclick="searchBlog();" /> = /> <input type="text" id="searchtext" name="searchtext" value=""<br /> <br /> true<br /> <br /> "search"<br /> <br /> "viewrandom"<br /> <br /> <div id="blog"></div> wpisy" <input type="button" id="showall" value="Pokaż wszystkie /> g();" "showBlo onclick= = wpis" <input type="button" id="viewrandom" value="Pokaż losowy /> log();" "randomB onclick= = </body> </html><br /> <br /> disabled<br /> <br /> false<br /> <br /> "showall"<br /> <br /> "disabled"<br /> <br /> jesteś tutaj  583<br /> <br /> Rozwiązanie magnetycznego ćwiczenia<br /> <br /> Magnesiki z JavaScriptem Korzystając z magnesików z kodem, uzupełnij kod strony MagicznaKostka, tak by umieszczone na niej przyciski były nieaktywne aż do momentu wczytania danych. Niektórych magnesików będziesz musiał użyć więcej niż jeden raz. <html> <head> k MagicznaKostka - blog miłośników przestrzennych układane <script type="text/javascript" src="ajax.js"> </script> <script type="text/javascript" src="date.js"> </script> <script type="text/javascript"> ... // Obsługa żądań przesyłanych przy użyciu Ajaksa function handleRequest() { 200) { if (ajaxReq.getReadyState() == 4 && ajaxReq.getStatus() == ... // Aktywujemy przyciski do obsługi blogu document.getElementById(<br /> <br /> "search"<br /> <br /> document.getElementById(<br /> <br /> "showall"<br /> <br /> ed disabl.... ). ........ ed .... disabl ). ........<br /> <br /> false<br /> <br /> =<br /> <br /> false<br /> <br /> =<br /> <br /> ed disabl.... andom" ). ........ "viewr........ document.getElementById(........ ...<br /> <br /> <body onload="loadBlog();"> k</h3> <h3>MagicznaKostka - blog miłośników przestrzennych układane <img src="cube.png" alt="MagicznaKostka" /> <input type="button" id="search" value="Przeszukaj blog" onclick="searchBlog();" /><br /> <br /> /> <input type="text" id="searchtext" name="searchtext" value="" <div id="blog"></div> wpisy" <input type="button" id="showall" value="Pokaż wszystkie<br /> <br /> "disabled" ...... onclick="showBlog();" /> disabled ..... = ........ ........ wpis" <input type="button" id="viewrandom" value="Pokaż losowy<br /> <br /> "disabled" ed = ........ ...... onclick="randomBlog();" /> disabl..... ........ </body> </html><br /> <br /> 584<br /> <br /> Rozdział 12.<br /> <br /> ;<br /> <br /> = ........ false .;<br /> <br /> } } ... </script> </head><br /> <br /> led" "disab..... ed = ........ disabl..... ........<br /> <br /> ;<br /> <br /> Dynamiczne dane<br /> <br /> Usprawnienia oszczędzające czas blogera Storna MagicznaKostka jest już sterowana dynamicznymi danymi, jednak Renia musi jeszcze poczekać, by w pełni skorzystać z wprowadzonych modyfikacji. A prawdziwe korzyści będzie w stanie czerpać dopiero w momencie, gdy uzyska możliwość wykorzystania internetowego interfejsu strony do dodawania nowych wpisów. Renia chce, by zamiast wprowadzać zmiany w pliku XML, mogła podać dane nowego wpisu na stronie, a następnie zapisać je na serwerze.<br /> <br /> Obrzydło mi już wpisywanie treści blogu w pliku i przesyłanie go FTP-em na serwer tylko po to, by wprowadzić jakąś drobną zmianę w treści blogu. Chciałabym móc zmieniać wpisy wygodnie — z poziomu przeglądarki.<br /> <br /> Edycja kodu + przesyłanie pliku na serwer = nic fajnego! Renia wyobraża sobie stworzenie strony tylko dla niej, która pozwalałaby jej łatwo dodawać nowe wpisy w blogu — wystarczyłoby wpisać treść i kilka dodatkowych informacji w formularzu na stronie. Dzięki takiemu rozwiązaniu dodanie nowego wpisu w blogu zawsze wymagałoby jedynie kilku kliknięć, a Renia potrzebowałaby tylko przeglądarki. Żadnych edytorów tekstów, żadnych programów do obsługi FTP — tylko przeglądarka i entuzjazm do pisania blogu i układania układanek.<br /> <br /> Dodanie nowego wpisu sprowadza się do wypełnienia kilku pól i kliknięcia przycisku.<br /> <br /> Nowa strona zawiera trz pola służące do wpisa y nia trzech informacji tworzą cych wpis w blogu.<br /> <br /> WYTĘŻ umysł<br /> <br /> W jaki sposób można wykorzystać technikę Ajax, by za pośrednictwem strony WWW dodawać nowe wpisy do danych XML?<br /> <br /> jesteś tutaj  585<br /> <br /> Klient i serwer… ponownie<br /> <br /> Zapisywanie danych blogu Myśląc o dodawaniu wpisów do blogu w kontekście techniki Ajax, można sobie wyobrazić rozwiązanie, w którym zostanie wygenerowane żądanie POST zawierające dane nowego wpisu, a następnie serwer zapisze te dane w pliku XML jako nowy wpis. W tym przypadku odpowiedź na żądanie nie musi być obsługiwana w żaden szczególny sposób, gdyż nie są zwracane żadne ważne informacje.<br /> <br /> Chwileczkę! A w jaki sposób ten nowy wpis zostanie zapisany w pliku blog.xml na serwerze? Sądziłam, że JavaScript nie może zapisywać plików. W końcu JavaScript jest technologią działającą po stronie klienta, prawda?<br /> <br /> JavaScript nie jest odpowiedzią na pytanie, jak zapisać plik na serwerze. Niewątpliwie JavaScript nie zapewni nam możliwości zapisania czegokolwiek w pliku blog.xml na serwerze. W rzeczywistości na serwerze nie możemy nawet wykonać jakiegokolwiek kodu JavaScript. Wynika to z prostego faktu, że język JavaScript jest technologią kliencką, zaprojektowaną tak, by działała wyłącznie w przeglądarkach WWW. W naszym przypadku JavaScript nie jest w stanie nam pomóc, gdyż nie może zapisywać plików na serwerze. Problem ten często się pojawia i właśnie dlatego wraz z JavaScriptem często są używane różnego rodzaju technologie serwerowe. A zatem potrzeba nam technologii podobnej do JavaScriptu, lecz działającej wyłącznie na serwerze. Można wskazać kilka takich technologii, lecz warto zwrócić szczególną uwagę na jedną z nich, która nie jest zbyt skomplikowana, a przy tym zapewnia doskonałe możliwości operowania na danych XML. Jest nią…<br /> <br /> 586<br /> <br /> Rozdział 12.<br /> <br /> Dynamiczne dane<br /> <br /> PHP spieszy z pomocą… tym razem Skryptowy język PHP oferuje wszystko, czego nam trzeba, by zapisać dane wpisu do pliku XML na serwerze. Cała operacja wymaga odczytania pliku XML, dodania nowego wpisu do już istniejących i w końcu zapisania wszystkich wpisów ponownie w tym samym pliku. Jednak wszystko zaczyna się od pobrania danych nowego wpisu, przesyłanych na serwer w żądaniu wygenerowanym przez skrypt działający w przeglądarce.<br /> <br /> PHP jest technologią skryptową działającą po stronie serwera.<br /> <br /> Data: 10/04/2008 Treść: "Nie mogę się doczekać …" Obrazek:<br /> <br /> Klient<br /> <br /> Nowy wpis jest przesyłany na serwer jako dane zapisane w żądaniu typu POST, wygenerowanym oczywiście przy użyciu Ajaksa.<br /> <br /> Data: 09/19/2008 Treść: O rany... zajęło mi to prawie... Obrazek: cube777.png Data: 09/24/2008 Treść: Śniło mi się dziś w nocy.... Obrazek: Data: 09/26/2008 Treść: Sny stają się coraz dziwniejsze... Obrazek: cubespart.png<br /> <br /> PHP spełnia podobną rolę co JavaScript, lecz działa na serwerze a nie w  przeglądarce. ,<br /> <br /> Serwer<br /> <br /> Data: 10/04/2008 Treść: "Nie mogę się doczekać…" Obrazek:<br /> <br /> erze Skrypt PHP wykonywany na serw pobiera dane wpisu i zapisuje je w pliku blog.xml. <blog> <title>... <author rel="nofollow">... <entries> <entry> ... </entry> ... </entries> </blog><br /> <br /> Język PHP możesz sobie wyobrazić jako odpowiednik JavaScriptu, który działa na serwerze i jest w stanie wykonywać określone przez Ciebie zadania… takie jak zapis danych blogu w pliku w postaci danych XML.<br /> <br /> blog.xml<br /> <br /> jesteś tutaj  587<br /> <br /> Serwerowe pyszności<br /> <br /> Gotowy kod JavaScript Sprawdzamy, czy plik z danymi blogu istnieje.<br /> <br /> Przekształcamy dane XML wczytane z pliku na strukturę danych, która przypomina drzewo DOM znane z JavaScriptu.<br /> <br /> Na serwerze, tam, gdzie przechowywana jest strona MagicznaKostka, znajduje się także skrypt PHP, który obsługuje operację dodawania nowego wpisu do danych XML blogu, które są przechowywane w pliku na serwerze. Wczytujemy dane XML z pliku do zmiennej $rawBlog.<br /> <br /> <?php $filename = "blog.xml";<br /> <br /> if (file_exists($filename)) { Jeśli plik blogu nie // Wczytujemy zawartość blogu z pliku XML istnieje, to twor zy ame); s($filen _content $rawBlog = file_get pusty dokument my nowy, XML. } else { // Tworzymy pusty dokument XML $rawBlog = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"; przestrzennych układanek"; $rawBlog .= "MagicznaKostka - blog miłośników es></entries></blog>"; r><entri k</autho układane zka miłośnic $rawBlog .= "<author rel="nofollow">Renia } Nowy wpis dodajemy $xml = new SimpleXmlElement($rawBlog); jako element potomny do struktury danych XML. // Dodajemy nowy wpis jako węzeł potomny ; "entry") ddChild( $entry = $xml->entries->a $entry->addChild("date", $_REQUEST["date"]); $entry->addChild("body", stripslashes($_REQUEST["body"])); if ($_REQUEST["image"] != "") $entry->addChild("image", $_REQUEST["image"]); // Zapisujemy cały blog do pliku $file = fopen($filename, 'w'); fwrite($file, $xml->asXML()); fclose($file); ?><br /> <br /> Nie ma<br /> <br /> P: Czy muszę używać PHP, by zapisywać pliki na serwerze?<br /> <br /> O: Ależ skąd. Dostępnych jest naprawdę<br /> <br /> sporo innych technologii służących do tworzenia skryptów działających na serwerze. Na przykład do dyspozycji masz między innymi język Perl (i skrypty CGI) oraz serwlety pisane w języku Java; wszystkie te technologie umożliwiają dokładnie to samo co PHP. Jeśli zatem lepiej znasz jedną z tych technologii, absolutnie nic nie stoi na przeszkodzie, byś to właśnie jej użył do stworzenia serwerowego komponentu dla swojej dynamicznej internetowej aplikacji korzystającej z techniki Ajax.<br /> <br /> 588<br /> <br /> Rozdział 12.<br /> <br /> Zapisujemy nowe dane blogu w pliku (usuwając jego dotychczasową zawartość).<br /> <br /> niemądrych pytań<br /> <br /> Ten skrypt PHP jest przechowywany na serwerze w pliku addblogentry.php.<br /> <br /> P: Czy mogę korzystać z techniki<br /> <br /> Ajax bez używania jakichkolwiek programów działających po stronie serwera?<br /> <br /> O: W pewnym sensie tak, choć w większości przypadków nie da się tego uniknąć. Pamiętaj, że niemal wszystkie aplikacje używające Ajaksa, może za wyjątkiem tych najprostszych, przesyłają na serwer jakieś dane i oczekują, że „serwer” je odbierze i coś z nimi zrobi (na przykład zapisze do bazy danych lub pliku). Strona MagicznaKostka jest dobrym przykładem aplikacji korzystającej z Ajaksa, która jednocześnie nie wymaga pisania żadnego złożonego skryptu działającego po stronie serwera.<br /> <br /> addblogentry.php<br /> <br /> Jednak przeważająca większość ajaksowych aplikacji nie ma tak dużo szczęścia. Oznacza to, że w większości przypadków nie unikniesz pisania jakiegoś kodu wykonywanego na serwerze. Najważniejszym pytaniem jest, czy wystarczy, by serwer przesłał w odpowiedzi zwartość całego pliku, jak jest w przypadku strony blogu Reni, czy też musi je w jakiś sposób przetworzyć. Na szczęście okazuje się, że skrypty działające po stronie serwera, których wymagają aplikacje korzystające z techniki Ajax, zazwyczaj są raczej proste i można je napisać nawet bez znajomości technologii serwerowych na poziomie profesjonalisty światowej klasy.<br /> <br /> Dynamiczne dane<br /> <br /> Także PHP ma swoje potrzeby W odróżnieniu od JavaScriptu, który jest domyślnie obsługiwany przez wszystkie nowoczesne przeglądarki, w przypadku języka PHP nie możemy z góry założyć, że będzie on dostępny na serwerze. A zatem zanim prześlesz pliki PHP na swój serwer, warto, byś zapytał administratora lub dostawcę usługi, czy jest na nim dostępny język PHP. Jeśli nie jest, to będziesz musiał zrobić wszystko, co w Twojej mocy, by go zainstalowano, bądź znaleźć inny serwer. Skrypt PHP obsługujący stronę MagicznaKostka nie będzie działał, jeśli na serwerze nie będzie dostępny język PHP.<br /> <br /> Uruchamianie skryptów PHP może wymagać wprowadzenia pewnych zmian na serwerze.<br /> <br /> PHP Upewnij się, że Twój serwer obsługuje skrypty PHP.<br /> <br /> Serwer<br /> <br /> Nawet jeśli nie obsługuje, to być może będziesz w stanie samemu zainstalować język PHP bądź namówić do tego administratora. Bez wątpienia będziesz potrzebował tej możliwości do obsługi swoich aplikacji korzystających z techniki Ajax!<br /> <br /> A zatem pierwszym problemem, z jakim musisz się uporać, jest obsługa skryptów PHP na używanym serwerze WWW. Drugim problemem jest określenie, w jakim miejscu na serwerze należy umieścić skrypty PHP. W wielu przypadkach można je umieszczać w tym samym katalogu, w którym są przechowywane pliki HTML i zewnętrzne skrypty JavaScript. Jednak niektóre instalacje środowiska PHP są bardziej wybredne i wymagają umieszczania skryptów w specjalnym katalogu. Także w tym przypadku administrator serwera na pewno pomoże Ci rozwiązać ten problem.<br /> <br /> w W większości przypadkó skrypty PHP mogą być samym przechowywane w tym jdują katalogu, w którym zna się strony WWW.<br /> <br /> Serwer<br /> <br /> www<br /> <br /> <html> <head> ... </head> <body> ... </body> </html><br /> <br /> Kiedy już określisz, gdzie na serwerze należy umieścić skrypt PHP, będziesz mógł go tam skopiować i zabrać się za wprowadzanie dalszych modyfikacji na stronie MagicznaKostka.<br /> <br /> youcube.html<br /> <br /> addblogentry.php<br /> <br /> <blog> <title>... <author rel="nofollow">... <entries> <entry> ... </entry> ... </entries> </blog><br /> <br /> blog.xml<br /> <br /> ajax.js<br /> <br /> jesteś tutaj  589<br /> <br /> Skrypt PHP — kolejny element układanki<br /> <br /> Przekazywanie danych do skryptu PHP Skoro serwer już obsługuje język PHP, a skrypt został umieszczony w odpowiednim miejscu, możemy spróbować dokładniej określić, czego będzie on potrzebował, by zapisać dane nowego wpisu w pliku XML na serwerze. Dzięki temu łatwiej nam będzie określić, jak powinny wyglądać przekazywane w żądaniu dane, by serwer mógł wykonać swoje zadanie. Skrypt PHP oczekuje przekazania danych nowego wpisu, które, jak wiemy, składają się z dwóch, ewentualnie trzech informacji.<br /> <br /> Data Data nowego wpisu.<br /> <br /> Data: 10/04/2008 …" Treść: "Nie mogę się doczekać zek: Obra<br /> <br /> Treść Treść wpisu.<br /> <br /> Obrazek<br /> <br /> u. Opcjonalny obrazek dodany do wpis<br /> <br /> Kod JavaScript działający w przeglądarce musi zapisać dane w formacie, który będzie można przesłać na serwer w żądaniu wysłanym przy użyciu Ajaksa.<br /> <br /> Te informacje musimy w jakiś sposób zapakować i przesłać na serwer w żądaniu wygenerowanym przy użyciu Ajaksa, gdzie zostaną one przetworzone i zapisane w pliku blog.xml.<br /> <br /> <blog> <title>... <author rel="nofollow">... <entries> <entry> ... </entry> ... </entries> </blog><br /> <br /> W tym momencie nowy wpis został już dodany do danych blogu i zapisany w pliku blog.xml. Oznacza to, że automatycznie pojawi się na stronie MagicznaKostka, kiedy ta zostanie wyświetlona w przeglądarce lub odświeżona.<br /> <br /> 590<br /> <br /> Rozdział 12.<br /> <br /> Dane są przekazywane do skryptu PHP za pośrednictwem żądania przesłanego przy użyciu techniki Ajax.<br /> <br /> blog.xml<br /> <br /> Zadaniem serwera jest odebranie żądania i przekazanie przesłanych w nim danych do skryptu PHP, który je odpowiednio przetworzy.<br /> <br /> Serwer<br /> <br /> <entry> <date>10/04/2008</date> <body>Nie mogę się doczekać...</body> <image></image> </entry><br /> <br /> Skrypt PHP zadba o skonwertowanie danych do postaci kodu XML i zapisanie ich w pliku blog.xml.<br /> <br /> A zatem stojące przed nami wyzwanie polega na zaprojektowaniu nowej strony WWW, która będzie zawierać elementy umożliwiające podanie wszystkich danych nowego wpisu, a także kod JavaScript, który pobierze te dane, przygotuje i prześle na serwer w żądaniu wysłanym przy użyciu techniki Ajax. Na szczęście okazuje się, że nie będziemy musieli w żaden szczególny sposób obsługiwać odpowiedzi przesłanej z serwera, no, może jedynie wyświetlimy komunikat o pomyślnym dodaniu nowego wpisu.<br /> <br /> Dynamiczne dane<br /> <br /> Zaostrz ołówek Naszkicuj wygląd nowej strony służącej do dodawania wpisów do blogu Reni; koniecznie wskaż, jakie miejsce w pełnym obiegu danych zajmują żądanie i odpowiedź.<br /> <br /> jesteś tutaj  591<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Naszkicuj wygląd nowej strony służącej do dodawania wpisów do blogu Reni; koniecznie wskaż, jakie miejsce w pełnym obiegu danych zajmują żądanie i odpowiedź. W tym przypadku używamy techniki Ajax, by wygenerować żądanie typu POST, które przekaże na serwer następujące dane:<br /> <br /> Strona służąca do dodawania wpis zawiera formularz, w którym możn ów a podać wszystkie dane wpisu.<br /> <br /> * datę wpisu, * treść wpisu, * dodany obrazek (ta informacja jest opcjonalna).<br /> <br /> Dane wpisu są przesyłane na serwer jako zawartość żądania typu POST.<br /> <br /> onclick!<br /> <br /> Data: 10/04/2008 Treść: "Nie mogę się doczekać…" Obrazek:<br /> <br /> Kliknięcie przycisku Dodaj nowy wpis powoduje przesłanie żądania na serwer.<br /> <br /> Klient informuje użytkownika o pomyślnym dodaniu nowego wpisu.<br /> <br /> żądanie Odpowiedź na dnych ża ra ie nie zaw klient danych, gdyż ch nie żadnych dany potrzebuje.<br /> <br /> 592<br /> <br /> Rozdział 12.<br /> <br /> <blog> <title>... <author rel="nofollow">... <entries> <entry> ... </entry> ... </entries> </blog><br /> <br /> blog.xml Serwer<br /> <br /> Serwer zapisuje nowy wpis sformatowany jako dane XML w pliku blog.xml.<br /> <br /> Dynamiczne dane<br /> <br /> Do rzeczy — przesyłanie danych wpisu na serwer Przesyłanie żądań typu POST przy użyciu techniki Ajax jest nieco bardziej skomplikowane niż wysyłanie żądań GET, a to dlatego, że wymaga przesłania na serwer danych. Żądania typu POST pozwalają na przesyłanie danych „spakowanych” na różne sposoby, jednak bardzo dobrze działa stare i sprawdzone kodowanie URL. Jest to ta sama technika, której przeglądarki używają do przesyłania na serwer danych dodanych do adresu URL strony. Jej cechą charakterystyczną jest oddzielanie poszczególnych informacji od siebie przy użyciu znaków &.<br /> <br /> Data: 10/04/2008 Treść: "Nie mogę się doczekać…" Obrazek:<br /> <br /> "date=10/04/2008&body=Nie mogę się doczekać... &image=..." e Poszczególne informacje oddzielon są od siebie znakami &.<br /> <br /> Każda informacja jest parą składającą się z nazwy i wartości .<br /> <br /> Ten format zapisu danych wymaga, by każda informacja miała postać pary nazwa – wartość, a jej elementy były od siebie oddzielone znakiem równości (=); z kolei poszczególne pary muszą być od siebie oddzielone znakiem &. Format ten określa się powszechnie mianem kodowania URL, posiada on także swój własny typ, który należy przesłać jako typ danych w żądaniu POST:<br /> <br /> "application/x-www-form-urlencoded; charset=UTF-8"<br /> <br /> To oficjalny typ danych wykorzystywany w przypadku przesyłania informacji zapisanych przy użyciu kodowania URL i musi zostać podany jako jeden z elementów żądania POST.<br /> <br /> Skoro zapisaliśmy już dane wpisu w wybranym formacie i określiliśmy jego typ w żądaniu POST, możemy zabrać się za pisanie kodu, który przygotuje dane i prześle żądanie na serwer, by przygotowany wcześniej skrypt PHP mógł je zapisać w pliku blog.xml.<br /> <br /> ćwiczenie<br /> <br /> Przygotuj przedstawione poniżej dane do przesłania, użyj przy tym kodowania URL, dzięki któremu łatwo je będzie przesłać w żądaniu POST. dataPremiery:13/01/1989 tytul: Gleaming the Cube <br /> <br /> rezyser: Graeme Clifford<br /> <br /> ...........................................................................................................................................................<br /> <br /> jesteś tutaj  593<br /> <br /> By zaspokoić Twoją ciekawość<br /> <br /> Przygotuj przedstawione poniżej dane do przesłania, użyj przy tym kodowania URL, dzięki któremu łatwo je będzie przesłać w żądaniu POST.<br /> <br /> ćwiczenie Rozwiązanie<br /> <br /> dataPremiery:13/01/1989<br /> <br /> tytul: Gleaming the Cube <br /> <br /> rezyser: Graeme Clifford<br /> <br /> "tytul=Gleaming the Cube&dataPremiery=13/01/1989&rezyser=Graeme Clifford " ...........................................................................................................................................................<br /> <br /> Nie ma<br /> <br /> niemądrych pytań<br /> <br /> P: Skoro skrypt umieszczony<br /> <br /> na stronie dodającej wpisy do blogu nie potrzebuje żadnych danych przesyłanych z serwera w odpowiedzi, to po co w ogóle obsługiwać tę odpowiedź?<br /> <br /> O: Otóż dlatego, że wciąż bardzo ważna<br /> <br /> jest sama informacja o zakończeniu obsługi żądania. A zatem choć serwer nie musi przekazywać żadnych informacji w odpowiedzi na żądanie, to jednak wciąż musimy wiedzieć, czy i kiedy żądanie zostało prawidłowo obsłużone. Tylko w ten sposób skrypt będzie wiedział, kiedy należy wyświetlić okienko dialogowe potwierdzające dodanie nowego wpisu.<br /> <br /> P: Czy do przekazania danych<br /> <br /> na serwer można by także użyć żądania typu GET?<br /> <br /> O: Tak, z technicznego punktu widzenia<br /> <br /> można tak zrobić. Istnieje możliwość przekazania danych na serwer w żądaniu typu GET, jednak w takim przypadku trzeba by je dodać bezpośrednio do adresu URL. To akurat nie jest trudne — problem polega na tym, że żądania tego typu nie powinny być używane, gdy zmienia się stan serwera. A w naszym przypadku nie ma żadnych wątpliwości, że stan serwera się zmienia — w końcu do pliku blog.xml zostanie dodany nowy wpis. A zatem w naszym przypadku właściwym<br /> <br /> 594<br /> <br /> Rozdział 12.<br /> <br /> rozwiązaniem będzie zastosowanie żądania typu POST, choćby dlatego, że w jasny sposób określa cel, w jakim żądanie jest przesyłane.<br /> <br /> P: Można przypuszczać, że<br /> <br /> obsłużenie żądania i zapisanie danych w pliku XML zajmie serwerowi pewien czas. Czy będzie zatem problemem, jeśli w momencie obsługi żądania (czyli zanim zostanie ono zakończone) przycisk Dodaj nowy wpis zostanie ponownie kliknięty?<br /> <br /> O: Owszem, to jest problem. Każde<br /> <br /> kliknięcie przycisku Dodaj nowy wpis powoduje przerwanie aktualnie realizowanego żądania i przesłanie nowego. Choć można sobie wyobrazić, że właśnie taki będzie cel powtórnego kliknięcia przycisku, to jednak działanie interfejsu użytkownika strony byłoby znacznie bardziej czytelne i zrozumiałe, gdyby użytkownik nie miał możliwości kliknięcia przycisku w czasie przetwarzania żądania. Oznacza to, że kod JavaScript powinien dezaktywować przycisk Dodaj nowy wpis na czas obsługi żądania przez serwer, a następnie ponownie go uaktywnić, gdy obsługa żądania się zakończy. Próby poprawienia intuicyjności i ułatwienia korzystania z interfejsu użytkownika naszej internetowej aplikacji mogą wymagać wielu takich drobnych usprawnień, jednak w ostatecznym efekcie jej użytkownicy będą znacznie bardziej zadowoleni.<br /> <br /> P: Co się dzieje ze znakami<br /> <br /> odstępu w danych zapisywanych przy użyciu kodowania URL? Wydaje mi się, że znaki odstępu w adresach URL czasami mogą przysparzać problemów.<br /> <br /> O: W tym przypadku znaki odstępu nie<br /> <br /> stanowią żadnego problemu, gdyż to Ajax automatycznie zajmuje się przetwarzaniem danych i dba o to, by dotarły one na serwer w odpowiedniej postaci.<br /> <br /> P: Skoro obrazek we wpisie jest opcjonalny, to czy zawsze należy go przesyłać na serwer podczas dodawania nowego wpisu?<br /> <br /> O: Nie, nie trzeba. Pamiętaj jednak, że<br /> <br /> przesłanie pustych danych, czyli pominięcie wartości za znakiem równości, to nic złego. Oto przykład: "date=...&body=...&image=” W tym przykładzie informacja o obrazku jest przesyłana na serwer, lecz nie zawiera żadnej wartości. W takim przypadku może się popisać skrypt PHP, gdyż jest on na tyle sprytny, by wiedzieć, że jeśli nie określono nazwy obrazka, to obrazek nie został dodany do wpisu.<br /> <br /> Dynamiczne dane<br /> <br /> Zaostrz ołówek Uzupełnij brakujące wiesze kodu, by dokończyć tworzenie kodu funkcji addBlogEntry() oraz handleRequest(), używanych na stronie służącej do dodawania wpisów do blogu Reni.<br /> <br /> function addBlogEntry() { // Dezaktywujemy przycisk Dodaj nowy wpis i informujemy, że aplikacja jest zajęta ................................................................ ................................................................<br /> <br /> // Przesyłamy dane nowego wpisu w żądaniu wygenerowanym przy użyciu Ajaksa ajaxReq.send("POST", "addblogentry.php", handleRequest, "application/x-wwwform-urlencoded; charset=UTF-8", ....................................................................... ....................................................................... .....................................................................); }<br /> <br /> // Obsługa odpowiedzi na żądanie przesłane przy użyciu Ajaksa function handleRequest() { if (ajaxReq.getReadyState() == 4 && ajaxReq.getStatus() == 200) { // Uaktywniamy przycisk Dodaj nowy wpis i czyścimy status ............................................................. .............................................................<br /> <br /> // Potwierdzamy dodanie nowego wpisu do blogu alert("Nowy wpis został prawidłowo dodany do blogu."); } }<br /> <br /> jesteś tutaj  595<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie<br /> <br /> Uzupełnij brakujące wiesze kodu, by dokończyć tworzenie kodu funkcji addBlogEntry() oraz handleRequest(), używanych na stronie służącej do dodawania wpisów do blogu Reni.<br /> <br /> Przycisk Dodaj nowy wpis jest dezaktywowany na czas zapisywania nowego wpisu w pliku na serwerze.<br /> <br /> W obszarze statusu strony wyś wiet jest komunikat „Dodajemy...”, infor lany użytkownika o tym, że powinien mujący trochę poczekać.<br /> <br /> function addBlogEntry() {<br /> <br /> // Dezaktywujemy przycisk Dodaj nowy wpis i informujemy, że aplikacja jest zajęta document.getElementById("add").disabled = true; ................................................................ document.getElementById("status").innerHTML = "Dodajemy..."; ................................................................ Generujemy żądanie typu POST.<br /> <br /> // Przesyłamy dane nowego wpisu w żądaniu wygenerowanym przy użyciu Ajaksa Do przetworzenia wpisu na serwer danych i zapisania ich ze w używany jest sk pliku rypt PHP.<br /> <br /> ajaxReq.send("POST", "addblogentry.php", handleRequest, "application/x-www-form-urlencoded; charset=UTF-8",<br /> <br /> "date=" + document.getElementById("date").value + ....................................................................... "&body=" + document.getElementById("body").value + ....................................................................... "&image=" + document.getElementById("image").value .....................................................................);<br /> <br /> Dane, które zostaną przesłane w żądaniu, powstają z połączenia zawartości trzech pól formularza: daty, treści oraz obrazka.<br /> <br /> }<br /> <br /> // Obsługa odpowiedzi na żądanie przesłane przy użyciu Ajaksa function handleRequest() { if (ajaxReq.getReadyState() == 4 && ajaxReq.getStatus() == 200) { // Uaktywniamy przycisk Dodaj nowy wpis i czyścimy status<br /> <br /> Tutaj upewniamy się, czy żądanie zostało pomyślnie obsłużone.<br /> <br /> document.getElementById("add").disabled = false; ............................................................. document.getElementById("status").innerHTML = ""; .............................................................<br /> <br /> // Potwierdzamy dodanie nowego wpisu do blogu alert("Nowy wpis został prawidłowo dodany do blogu."); } }<br /> <br /> 596<br /> <br /> Rozdział 12.<br /> <br /> został Teraz, kiedy wpisny, możemy pomyślnie zapisa ić przycisk ponownie uaktywni wyczyścić Dodaj nowy wpis obszar statusu.<br /> <br /> Dynamiczne dane<br /> <br /> Łatwe blogowanie Renia wprost nie może uwierzyć, jak wielką różnicę sprawia możliwość aktualizacji danych bez konieczności otwierania pliku, edycji kodu XML i ponownego przesyłania pliku na serwer. Jej blog jest aktualnie w pełni sterowany danymi, a sama Renia czuje się znacznie bardziej zainspirowana do tworzenia nowych wpisów!<br /> <br /> Strona informuje użytkownika, że nowy wpis właśnie jest zapisywany.<br /> <br /> Renia tworzy nowe wpisy na stronie służącej specjalnie do ich Nowy wpis pojawia dodawania. się na stronie MagicznaKostka.<br /> <br /> Strona potwierdza, że nowy wpis został pomyślnie zapisany.<br /> <br /> Dynamiczne zapisywanie danych w blogu jest po prostu niesamowite!<br /> <br /> jesteś tutaj  597<br /> <br /> Łatwość używania jest najważniejsza<br /> <br /> Ułatwienie korzystania ze strony blogu Nie stałbyś się posiadaczem czarnego pasa mistrzowskiego w układaniu kostek, gdyby nie wyjątkowa dbałość o szczegóły. Nic zatem dziwnego, że także Renia chce doprowadzić swoją stronę do całkowitej doskonałości. Renia wie, że aplikacje korzystające z techniki Ajax są znane ze swojej dbałości o szczegóły, zwłaszcza jeśli chodzi o kwestie związane z łatwością użytkowania. Dlatego też Renia chciałaby usprawnić swoją nową stronę, tak by była porównywalna z nowoczesnymi stronami WWW.<br /> <br /> Chciałabym poprawić wydajność dodawania wpisów, tak bym mogła tworzyć je szybciej i, miejmy nadzieję, częściej.<br /> <br /> Usprawnianie dodawania nowych wpisów Ponieważ przeważająca większość wpisów jest tworzona na bieżąco, zatem Renia doszła do wniosku, że zaoszczędzi chwilkę czasu, jeśli automatycznie wyświetli w polu formularza bieżącą datę. Co więcej, ponieważ większość wpisów będzie dodawana z bieżącą datą, zatem chciałaby, żeby miejsce wprowadzania zostało umieszczone w polu służącym do podawania treści. Dzięki temu Renia będzie mogła zacząć wpisywać treść bezpośrednio po wyświetleniu strony. Oczywiście, żadna z tych zmian nie ma krytycznego znaczenia dla działania blogu ani nie jest bezpośrednio powiązana z techniką Ajax; obie jednak znacznie ułatwiają korzystanie ze strony, co jest całkowicie zgodne z „duchem” Ajaksa. Co więcej, obie zmiany zapewnią, że Renia będzie regularnie dodawać nowe wpisy do swojego blogu, dzięki czemu będzie on aktualny. W tym polu automatycznie zapiszemy aktualną datę.<br /> <br /> Tutaj umieścimy miejsce wprowadzania, by Renia mogła natychmiast zacząć wpisywanie treści blogu.<br /> <br /> 598<br /> <br /> Rozdział 12.<br /> <br /> Dynamiczne dane<br /> <br /> W ramach ułatwienia automatycznie wypełniaj pola formularzy Jak sobie zapewne przypominasz, format, w jakim należy wpisywać daty na stronie MagicznaKostka, ma postać MM/DD/RRRR. Musimy się zatem upewnić, że data, którą automatycznie umieścimy w polu formularza, będzie zapisana właśnie w taki sposób. Oznacza to, że będzie nam potrzebny jakiś kod, który zapisze datę w formacie MM/DD/RRRR.<br /> <br /> Masz już coś bardzo podobnego — napisałeś nową metodę obiektu Date, wyświetlającą datę w formacie DD/MM/RRRR, z tym że jej kod jest aktualnie umieszczony na głównej stronie MagicznaKostka. Może mógłbyś zatem napisać jeszcze jedną metodę, zapisującą datę w nieco inny sposób, a następnie użyć tego kodu na stronie służącej do dodawania nowych wpisów?<br /> <br /> Wielokrotne stosowanie fragmentów skryptu jest doskonałym pomysłem, gdyż pozwala uniknąć powielania kodu. Bez wątpienia nie chcemy, by w aplikacji Reni pojawiły się powielone fragmenty tego samego kodu, którymi musielibyśmy się zajmować w kilku różnych miejscach, natomiast optymalnie by było, gdyby kody obu metod formatujących daty znajdowały się w jednym miejscu. Właśnie z tego powodu ponowne wykorzystanie napisanego już kodu formatującego datę na dwóch stronach jest doskonałym pomysłem. Nie będzie też większego problemu ze znalezieniem sposobu na zapisanie kodu w jednym miejscu i używaniem go na wielu różnych stronach WWW.<br /> <br /> WYTĘŻ umysł<br /> <br /> W jaki sposób zapewniłbyś możliwość korzystania z kodu metody shortFormat() oraz nowej metody shortInputFormat() na obu stronach MagicznaKostka?<br /> <br /> jesteś tutaj  599<br /> <br /> Import — eksport<br /> <br /> Wielokrotnie wykonywane zadanie? Może by tak jakaś funkcja pomogła? Zapewnienie możliwości skorzystania z tego samego kodu JavaScript na kilku stronach WWW wiąże się z koniecznością umieszczenia go w osobnym pliku (albo module), który następnie będzie importowany na każdej ze stron. Rozwiązanie to zastosowaliśmy już w przypadku obiektu AjaxRequest, którego kod zapisaliśmy w pliku ajax.js, a następnie zaimportowaliśmy przy użyciu poniższego znacznika:<br /> <br /> Doskonale nam znany znacznik <script> służy do importowania kodu JavaScript zapisanego w zewnętrznych plikach.<br /> <br /> jest Nazwa pliku z kodem JavaScript src podawana jako wartość atrybutu znacznika <script>.<br /> <br /> <script type="text/javascript" src="ajax.js"> </script><br /> <br /> Nasza metoda shortInputFormat() obiektu Date będzie działać równie dobrze także wtedy, gdy umieścimy ją w zewnętrznym pliku (na przykład o nazwie date.js), po czym zaimportujemy na stronach MagicznaKostka.<br /> <br /> Date.prototype.shortInputFormat = function() { + this.getFullYear(); return (this.getMonth() + 1) + "/" + this.getDate() + "/" }<br /> <br /> Ten sam znacznik <script> możemy teraz zastosować na każdej stronie MagicznaKostka, by zaimportować do niej kod JavaScript umieszczony w pliku date.js.<br /> <br /> Rozdział 12.<br /> <br /> function doY() { ... }<br /> <br /> Dzięki umieszczeniu w  zewnętrznym pliku ten sam kod JavaScript może być teraz używany w wielu mie jscach.<br /> <br /> Umieszczanie kodu, który potencjalnie może się powtarzać, w osobnym pliku JavaScript, tak by można go wielokrotnie używać w różnych miejscach, zawsze jest dobrym rozwiązaniem.<br /> <br /> 600<br /> <br /> function doX() { ... }<br /> <br /> date.js<br /> <br /> <script type="text/javascript" src="date.js"> </script> Za pomocą tego prostego znacznika możemy zaimportować na stronę całą zawartość pliku date.js.<br /> <br /> var x; var y;<br /> <br /> Dynamiczne dane<br /> <br /> <div id="blog"></div> Metoda shortFormat() formatuje daty prezentowane na głównej stronie MagicznaKostka.<br /> <br /> /> <input type="text" id="date" name="date" value="" size="10"<br /> <br /> Metoda shortInputFormat() używana jest na stronie służącej do dodawania nowych wpisów, gdzie korzystamy z niej, by automatycznie zapisać aktualną datę w polu formularza.<br /> <br /> Zaostrz ołówek Napisz kod nowej funkcji o nazwie initForm(), która będzie wywoływana w procedurze obsługi zdarzeń onload nowej strony do dodawania wpisów. Zadaniem tej funkcji jest inicjalizacja pola daty, czyli zapisanie w nim aktualnej daty oraz ustawienie miejsca wprowadzania w polu służącym do podania treści wpisu. .................................................................................................................................................................... .................................................................................................................................................................... .................................................................................................................................................................... ....................................................................................................................................................................<br /> <br /> jesteś tutaj  601<br /> <br /> Rozwiązanie ćwiczenia<br /> <br /> Zaostrz ołówek Rozwiązanie W polu daty automatycznie zapisywana jest bieżąca data.<br /> <br /> Napisz kod nowej funkcji o nazwie initForm(), która będzie wywoływana w procedurze obsługi zdarzeń onload nowej strony do dodawania wpisów. Zadaniem tej funkcji jest inicjalizacja pola daty, czyli zapisanie w nim aktualnej daty oraz ustawienie miejsca wprowadzania w polu służącym do podania treści wpisu.<br /> <br /> function initForm() { .................................................................................................................................................................... Miejsca document.getElementById("date").value = (new Date()).shortInputFormat(); .................................................................................................................................................................... wprowadzania umieszczamy document.getElementById("body").focus(); w polu służącym .................................................................................................................................................................... do podania treści } .................................................................................................................................................................... nowego wpisu.<br /> <br /> Rośnie produktywność blogowania Strona MagicznaKostka w końcu spełnia wszystkie oczekiwania Reni. Dzięki zastosowaniu techniki Ajax oraz przywiązaniu uwagi do najdrobniejszych szczegółów, na co mogła się zdobyć jedynie mistrzyni układanek, blog Reni jest w pełni sterowany danymi, przyjazny i łatwy w użytkowaniu.<br /> <br /> Naprawdę ubóstwiam swój blog!<br /> <br /> 602<br /> <br /> Rozdział 12.<br /> <br /> W momencie wyświet lan miejsce wprowadzania ia strony umieszczane w polu słujest do podania treści now żącym ego wpisu.<br /> <br /> W polu daty podczas wyświetlania strony automatycznie zapisywana jest aktualna data.<br /> <br /> Dynamiczne dane<br /> <br /> Zaginarka stron Zegnij stronę wzdłuż pionowych linii, tak by oba mózgi się połączyły, a następnie rozwiąż zagadkę.<br /> <br /> Co Renia zyskała dzięki zastosowaniu Ajaksa? To spotkanie umysłów!<br /> <br /> AjaxRequest<br /> <br /> Rozpadam się… kawałeczek po kawałeczku…<br /> <br /> Bez obaw, zaraz cię z powrotem poskładam.<br /> <br /> Aż trudno opisać, jak wiele korzyści dało Reni zastosowanie techniki Ajax. Przede wszystkim jej dane stały się dynamiczne, a z punktu widzenia Reni oznacza to, że dodawanie wpisów do blogu stało się znacznie łatwiejsze i szybsze. Teraz blogowanie o swych układankowych osiągnięciach nie przysparza już Reni najmniejszych kłopotów.<br /> <br /> jesteś tutaj  603<br /> <br /> Kontynuujemy naszą podróż<br /> <br /> Gdzie dalej możesz się udać? A zatem dotarłeś do końca książki Head First JavaScript i jesteś gotów, by kontynuować swoją podróż w kierunku tworzenia interaktywnych stron WWW przy użyciu języka JavaScript… a może nawet dalej. Ale gdzie teraz powinieneś się udać? Poniżej podaliśmy kilka miejsc, które, jak przypuszczamy, mogą Cię zainteresować, gdy będziesz kontynuować naukę tworzenia aplikacji internetowych.<br /> <br /> W lewo czy w prawo? Czas na instrukcję if/else!<br /> <br /> ZAJRZYJ na forum Head First JavaScript Wyrażenia doprowadziły Cię do skrajnej desperacji? A może przytłoczyły Cię operatory? A może po prostu chcesz się podzielić swoimi ostatnimi skryptami ze społecznością czytelników Head First JavaScript? Jeśli tak, to zajrzyj na chwilę na forum Head First JavaScript (na witrynie http://www.headfirstlabs.com) i dołącz do jednej z prowadzonych na nim dyskusji… albo rozpocznij własną!<br /> <br /> PRZECZYTAJ inną książkę Opanowałeś już podstawy, zatem możesz zacząć poznawać bardziej zaawansowane tajniki stosowania języka JavaScript. JavaScript. Wprowadzenie Ajax. Implementacje<br /> <br /> DOWIEDZ się czegoś więcej, przeglądając inne witryny WWW QuirksMode www.quirksmode.org Niestety zdarza się, że przeglądarki wykonują pewne operacje przy użyciu JavaScriptu na swój własny sposób. Na witrynie QuirksMode znajdziesz informacje na temat rozbieżności w działaniu najpopularniejszych przeglądarek.<br /> <br /> Dokumentacja języka JavaScript http://developer.mozilla.org/pl/docs/JavaScript Zapewne już niebawem zejdziesz z utartej ścieżki nauki i podejmiesz wyzwanie samodzielnego pisania skryptów w języku JavaScript. Aby mu sprostać, przyda Ci się dokładniejsza znajomość wbudowanych obiektów JavaScriptu. Wszelkie ich tajniki odkryjesz dzięki stronie z dokumentacją języka, prowadzonej przez fundację Mozilla.<br /> <br /> Prototype — framework JavaScript http://www.prototypejs.org Czy kusi Cię, by zastosować jakąś wbudowaną bibliotekę i przenieść swoje umiejętności korzystania z języka JavaScript na zupełnie nowy poziom? Jeśli tak, to framework Prototype jest jednym z najlepszych, jakie istnieją, a co więcej — za korzystanie z niego nie trzeba nic płacić!<br /> <br /> 604<br /> <br /> Rozdział 12.<br /> <br /> Skorowidz<br /> <br /> Skorowidz --, 216, 224 !, 188, 240 !=, 187, 188 $, 78 &&, 239, 240 /* */, 191 //, 190, 191 [], 254 _, 78 {}, 172 ||, 240 +, 104 ++, 214, 224 <, 188 <=, 188 <div>, 47, 362 <head>, 41 <img>, 226, 488 <input>, 311, 582 <script>, 41, 600 src, 145 type, 56 <span>, 320, 389 ==, 187, 188 >, 188 >=, 188<br /> <br /> A<br /> <br /> abort(), 560 abs(), 451 adres e-mail, 352 URL, 593 Ajax, 41, 115, 545, 549, 558 AjaxRequest, 564 DOM, 574 funkcja obsługi żądania, 572, 576<br /> <br /> GET, 562 kod statusu HTTP, 560 obsługa żądania, 573 odbieranie odpowiedzi, 564 odpowiedź, 558, 560 POST, 562 przekazywanie danych do skryptu PHP, 590 przerywanie żądania, 560 przesyłanie żądań, 564, 569 stan żądania, 580 status żądania, 580 tworzenie obiektu żądania, 571 upraszczanie, 564 XML, 556 XMLHttpRequest, 560, 561 zmiana stanu żądania, 560 żądania, 558, 567 AjaxRequest, 564, 567, 568 getResponseText(), 573 getResponseXML(), 573 send(), 564, 568 akcja, 36 aktywność mózgu, 23 alert(), 44, 46, 48, 49, 51, 118, 332 alternatywa logiczna, 241 AND, 239, 240, 241 aplikacje, 291 apostrofy, 512 appendChild(), 377, 400 argumenty, 269, 272, 273, 279 Array, 220, 434 sort(), 437, 446 ASP.NET AJAX, 41 asynchroniczne przesyłanie żądań, 568 asynchroniczny JavaScript i XML, 556 Asynchronous JavaScript And XML, 556<br /> <br /> jesteś tutaj  605<br /> <br /> Skorowidz<br /> <br /> bezimienne funkcje, 302 bezpieczeństwo, 43 ciasteczka, 138, 150 białe znaki, 338 blog, 422 blok kodu, 166, 172 blur, 314 błędy, 495, 502 czasu wykonywania, 534, 535 logiczne, 520, 522, 535 programistyczne, 88 składniowe, 510, 521, 522, 535 body.clientHeight, 130 body.clientWidth, 130 break, 202, 203, 234, 235, 246<br /> <br /> sprawdzanie obsługi w przeglądarce, 152 usuwanie, 144 wartość, 143 zapisywanie, 148 class, 388 className, 388, 389 clearInterval(), 123 clientHeight, 130 clientWidth, 130 compare(), 436 const, 74, 77 continue, 234 cookieEnabled, 152 cookies, 114, 138 createElement(), 399, 400, 401 createTextNode(), 377, 399 CSS, 36, 38, 47, 113, 388 cudzysłowy, 511, 512 cykl życia skrypty, 137 zmienne, 193 cykliczne liczniki czasu, 119, 123 cykliczne wykonywanie fragmentu kodu, 121 czas, 118, 424 czas opóźnienia, 119, 120 czujka, 518<br /> <br /> C<br /> <br /> D<br /> <br /> atrybuty, 297, 512 id, 99, 104, 311 name, 311 onclick, 297 size, 327 wartości, 553 ATTRIBUTE, 370 automatyczna obsługa typów danych, 77 automatyczne wypełnianie pól formularzy, 599<br /> <br /> B<br /> <br /> CamelCase, 80, 90 case, 201 ceiling(), 448 charAt(), 440, 446 childNodes, 373, 379 chwilowe wyłączanie kodu, 538 ciało funkcji, 267 ciasteczka, 114, 138, 140 bezpieczeństwo, 150 data wygaśnięcia, 143, 144, 151 nazwy, 143, 147, 151 odczyt danych, 144 przechowywanie danych, 142 sesja przeglądarki, 151<br /> <br /> 606<br /> <br /> Skorowidz<br /> <br /> dane, 61, 62, 292 dynamiczne, 546 formularze, 311 liczbowe, 92 sprawdzanie poprawności, 313, 315, 323 tabelaryczne, 255 tymczasowe, 197 wpisane przez użytkownika, 35 XML, 551, 552, 580 Data, 123 data wygaśnięcia ciasteczka, 143 data-driven, 547 Date, 424, 428, 482 getDate(), 431 getFullYear(), 431<br /> <br /> Skorowidz getMonth(), 431 parse(), 424 daty, 333, 334, 426 Date, 424 formatowanie, 480 DebugConsole, 533 debugowanie, 496 czujka, 518 Firebug, 500 Firefox, 501 konsola błędów, 526 narzędzia, 521 okna dialogowe, 517 upraszczanie skryptu, 537 watch, 518 decyzje, 159, 161 dekrementacja, 216, 224 dezaktywacja przycisków, 582 diagnozowanie, 500 disabled, 582 display, 395 div, 47, 362 długość danych, 324 długość tekstu, 323 document, 126, 127 DOCUMENT, 370 Document Object Model, 359, 368 document.body, 127 document.createElement(), 399, 400 document.createTextNode(), 399 document.getElementById(), 54, 99 dodawanie arytmetyczne, 92 dodawanie elementów do tablic, 256 dodawanie kodu JavaScript, 41 dodawanie obrazków do strony, 490 dokumentacja, 190 język JavaScript, 604 DOM, 359, 368, 369, 388, 557, 574 appendChild(), 377, 400 ATTRIBUTE, 370 childNodes, 373, 379 className, 388 createTextNode(), 377<br /> <br /> DOCUMENT, 370 dostęp do klasy CSS, 388 drzewo, 380 ELEMENT, 370 firstChild, 373, 376 klasyfikacja węzłów drzewa, 370 lastChild, 373 modyfikacja wartości stylów CSS, 388 modyfikacja węzła tekstowego, 376 nodeType, 373 nodeValue, 373 operacje na drzewie, 401 podmiana stylów, 389 poruszanie się po drzewie, 373 potomek, 369 removeChild(), 377 style, 393 style CSS, 388 TEXT, 370 tworzenie elementów HTML, 399, 400 węzły, 369, 370 węzły potomne, 379 właściwości, 373, 375 dopasowywanie wzorców, 336 dostęp do danych formularzy, 311 dostęp do elementów HTML, 362, 363 dostęp do klasy CSS, 388 dostęp do składowych, 410 dozwolone nazwy, 79 drzewo decyzyjne, 176 drzewo węzłów, 369 drzewo XML, 557 duże problemy, 264, 265 dynamiczne dane, 546 dynamiczne opcje nawigacyjne, 386 dynamiczne pobieranie danych z serwera, 549<br /> <br /> E<br /> <br /> efektywność, 275 ELEMENT, 370 elementy stron WWW, 104 else, 166, 174 eraseCookie(), 143, 144<br /> <br /> jesteś tutaj  607<br /> <br /> Skorowidz<br /> <br /> F<br /> <br /> false, 63, 174, 188, 219 fałsz, 63 Firebug, 500 Firefox, 499, 500, 501 firstChild, 373, 376 floor(), 448, 450, 451 focus, 314 for, 213, 214, 248 akcja, 213 aktualizacja, 213 inicjalizacja, 213 licznik, 214 warunek, 213 form, 312, 325 formatowanie dat, 480 formularze, 84, 308 dane, 311 dostęp do danych, 311 form, 312, 325 identyfikacja pól, 311 pasywny system pomocy, 320 pobieranie danych, 99 pola, 311, 312 przesyłanie danych na serwer, 332 sprawdzanie poprawności danych, 313, 315, 323 sprawdzanie wypełnienia pól, 318 weryfikacja danych, 100, 312, 313, 314 weryfikacja długości danych, 324 wprowadzanie informacji, 313 zdarzenia, 314 function, 267 funkcja porównująca, 435 funkcje, 49, 51, 173, 181, 263, 266, 296 alert(), 48, 49 anonimowe, 299 argumenty, 269, 272, 273, 279 ciało, 267 isNaN(), 100 liczba argumentów, 280 modyfikacja wartości argumentów, 280 nazwy, 269<br /> <br /> 608<br /> <br /> Skorowidz<br /> <br /> obsługa żądania, 572, 576 odwołania do funkcji, 293 parametry, 51 parseFloat(), 93 parseInt(), 93, 104, 105 referencje, 293 return, 282, 283, 284 setTimeout(), 119, 120, 121, 130 tworzenie, 267 wiele wartości wynikowych, 283 wywołanie, 293 zwracanie wartości, 269, 281, 282 funkcje zwrotne, 295, 296, 297, 301 zdarzenia, 297 funkcjonalność, 291<br /> <br /> G<br /> <br /> generowanie liczb losowych, 448, 450 GET, 561, 562, 563, 569 getDate(), 431 getElementById(), 54, 99, 104, 311, 363, 365, 368, 373, 374 getElementsByTagName(), 363, 374 getFullYear(), 431 getMonth(), 431 getReadyState(), 564 getResponseText(), 564, 573 getResponseXML(), 564, 573 getStatus(), 564 getText(), 575 getTime(), 144 GIF, 171<br /> <br /> H<br /> <br /> handler, 567 handleRequest(), 576 height, 128 hermetyzacja, 484 hipertekst, 550 historia decyzji, 398 HTML, 36, 38<br /> <br /> Skorowidz<br /> <br /> I<br /> <br /> id, 99, 104, 311, 327, 362, 363, 368 identyfikacja pól formularzy, 311 identyfikatory, 78, 90 if, 162, 174 blok kodu, 166 else, 166, 167, 174 warunek logiczny, 163 wybór opcji, 165 zagnieżdżanie instrukcji if, 179 importowanie kodu JavaScript, 145, 600 indeks, 220 indexOf(), 440, 441, 446 informacje o dacie, 431 informacje zwrotne, 84 informacje zwrotne z funkcji, 280 informacyjne okienko dialogowe, 49, 315 informowanie o czynnościach wykonywanych przez użytkownika, 117 informowanie użytkownika, 49 inicjalizacja stałe, 88 zmienne, 73 inkrementacja, 214, 224 innerHTML, 364, 366, 368, 380, 381, 387 input, 311 instancja, 464 instrukcje, 167, 173 złożone instrukcje, 172 interaktywne opcje decyzyjne, 387 interaktywne strony WWW, 36 interaktywność, 31, 33, 38, 116 interfejs użytkownika, 105, 360 Internet Explorer, 498, 501 internetowy interfejs strony, 585 interpreter języka JavaScript, 112 intuicyjność interfejsu użytkownika, 105 isNaN(), 100<br /> <br /> J<br /> <br /> JavaScript, 34, 36, 112, 113 jednokrotny licznik czasu, 130 język<br /> <br /> JavaScript, 34, 36 kliencki, 112 PHP, 587 skryptowy, 41 XHTML, 553 XML, 550, 555<br /> <br /> K<br /> <br /> kaskadowe arkusze stylów, 113 klasy, 464, 468, 471 hermetyzacja, 484 metody, 483 prototype, 468 właściwości, 473 klasy CSS, 388, 391, 395 klasy znaków, 354 klasyfikacja węzłów drzewa DOM, 370 klient, 112, 114 WWW, 127 klucze, 220, 221 kod CSS, 47 JavaScript, 41, 43, 112 XML, 580 kod statusu HTTP, 560 kodowanie URL, 593, 594 kody pocztowe, 329 kojarzenie elementów tablicy z obrazkami, 226 komentarze, 190, 191, 197, 538, 541 chwilowe wyłączanie kodu, 538 jednowierszowe, 191 wielowierszowe, 191 komunikacja dwustronna, 53 komunikaty o błędach, 315 koniec wiersza kodu, 49 koniunkcja logiczna, 241 konkatenacja łańcuchów znaków, 92 konsola błędów, 501, 526 konsola do testowana skryptów, 527 konstruktor, 416, 417, 471 nazwa, 417 kontekst danych, 193 kontrolowanie przeglądarki, 115<br /> <br /> jesteś tutaj  609<br /> <br /> Skorowidz konwersja łańcuchy znaków na liczby, 93 obiekty na łańcuchy znaków, 430 kropka, 410 kwantyfikatory, 340<br /> <br /> L<br /> <br /> lastChild, 373 length, 325, 434, 440 liczby, 63, 92 losowe, 448, 450 licznik pętli, 214 liczniki cykliczne, 119 liczniki czasu, 114, 118 setTimeout(), 120 lista unikania błędów, 542 literały funkcyjne, 292, 299, 302, 436 literały obiektowe, 427 literówki, 506, 507 location, 123 lokalizacja zmiennych, 192, 194 losowy wybór wpisów blogu, 447<br /> <br /> Ł<br /> <br /> łańcuchy znaków, 63, 446, 511 konkatenacja, 92 konwersja na liczby, 93 String, 440<br /> <br /> M<br /> <br /> Math, 448, 449, 451 Math.ceiling(), 448 Math.floor(), 450 Math.random(), 448, 450 Math.round(), 448 max(), 451 maxLength, 324, 327 mechanizm rejestracji historii, 399 mechanizm trwałego przechowywania danych, 142 metapoznanie, 23 metaznaki, 338, 339<br /> <br /> 610<br /> <br /> Skorowidz<br /> <br /> metody, 99, 410, 412, 415, 455 przeciążanie, 463 toString(), 430, 437 metody klasowe, 470, 483 tworzenie, 483 wywoływanie, 486 metody weryfikacji danych, 321 min(), 451 minLength, 324, 327 moduły, 51 modyfikacja strony WWW, 359 style CSS, 393 wartości stylów CSS, 388 węzły tekstowe, 376 mózg, 21 myślenie, 21<br /> <br /> N<br /> <br /> name, 311 NaN, 90, 97, 518, 536 nauczanie, 21 navigator.cookieEnabled, 152, 155 nawiasy klamrowe, 172, 254, 504 nazwy, 78, 79, 97 ciasteczka, 143, 151 funkcje, 269 konstruktory, 417 obiekty, 411, 471 stałe, 74 zmienne, 72 negacja, 189 negacja logiczna, 241 new, 416, 427 niedozwolone nazwy, 79 nieskończone pętle, 223, 232 niestandardowe obiekty, 415 niezawodność aplikacji, 307 niezdefiniowany element danych, 89 nodeType, 373 nodeValue, 373, 376 NOT, 240, 241<br /> <br /> Skorowidz not defined, 507 notacja CamelCase, 80, 90 notacja obiektowa, 412 null, 189, 321, 489, 507 numery telefonów, 351<br /> <br /> O<br /> <br /> obiektowy, 473 Obiektowy Model Dokumentu, 359, 369, 380 obiekty, 99, 220, 407, 412, 413, 415 AjaxRequest, 564 Array, 220, 434 dane, 409 Date, 424 document, 126 dostęp do składowych, 410 form, 312, 325 hermetyzacja, 484 konstruktor, 416 konwersja na łańcuchy znaków, 430 Math, 448, 451 metody, 410 niestandardowe, 415 organizacyjne, 448 porównywanie, 437 prototype, 468, 480, 481 przeciążanie metod, 463 RegExp, 344 rozszerzanie obiektów, 481 składowe, 410 String, 427, 440 style, 128, 395 this, 417 tworzenie, 416, 419 właściwości, 410 XMLHttpRequest, 560 Object-Oriented Programming, 473 obliczenia, 86 obrazy, 226, 488 PNG, 171 obserwacja zmiennych, 518, 526 obsługa kodu JavaScript w przeglądarce WWW, 42 obsługa skryptów PHP, 589<br /> <br /> obsługa zdarzeń, 51, 57, 297 obsługa żądania Ajax, 573 oczekiwania użytkowników, 115 oddzielanie kodu JavaScript od kodu HTML, 303 odpowiedź, 558 odwołanie do funkcji, 293, 298 odwołanie do składowych, 411 okna dialogowe, 44, 49 testowanie programu, 517 okno klienta, 126, 127 okno przeglądarki, 126, 127 określanie procedura obsługi zdarzeń, 298 typ danych zmiennej, 73 wymiar obrazka, 128 onblur, 44, 313, 314, 316, 325 onchange, 313, 316 onclick, 44, 48, 54, 57, 297 onfocus, 313 onload, 51, 298, 303 onmouseout, 391 onmouseover, 391 onreadystatechange, 560, 561 onresize, 133, 134 OOP, 473 opcje decyzyjne, 387 opcje nawigacyjne, 386 open(), 560, 561 Opera, 499 operatory AND, 239, 240 jednoargumentowy, 241 kropka, 410, 411 logiczne, 188, 240 new, 416, 419, 427 NOT, 240 OR, 240 porównania, 188 równość, 187 różny od, 187 opis sceny, 362 OR, 240, 241 osadzanie kodu JavaScript, 41<br /> <br /> jesteś tutaj <br /> <br /> 611<br /> <br /> Skorowidz<br /> <br /> P<br /> <br /> parse(), 424 parseFloat(), 93, 97 parseInt(), 93, 97, 104, 105 pary nawiasów, 508 pasywny system pomocy, 320, 325 pętle, 211 break, 234 continue, 234 for, 213 nieskończone, 223, 232 przejście do kolejnego cyklu, 234 przerwanie działania, 234 warunek zakończenia, 233 while, 244, 245, 249 wybór, 247 zagnieżdżanie, 249, 257 PHP, 587 przekazywanie danych do skryptu, 590 uruchamianie skryptów, 589 PI, 448 planowanie interakcji, 46 pliki XML, 552 pluskwa, 496 PNG, 171 pobieranie danych z formularza, 99 pobieranie informacji od użytkownika, 54 pobieranie zawartości elementów HTML, 364 podejmowanie decyzji, 159, 161, 178 operatory porównania, 188 podglądanie wartości zmiennej, 517 podłańcuchy, 440 podmiana stylów, 389 pola formularza, 100, 311, 312 pola tekstowe, 324 dopuszczalna długość tekstu, 324 poprawianie błędów, 500 poprawne dane, 310 porównywanie, 188 obiekty, 437 poruszanie się po drzewie DOM, 373 POST, 561, 562, 563, 569, 586<br /> <br /> 612<br /> <br /> Skorowidz<br /> <br /> potomek, 369 potrzeby użytkowników, 32 powielanie kodu, 290, 479, 599 powitanie, 50 powtórzenia kodu, 274 poziom skryptu, 197 prawda, 63 prezentacja, 291 procedury obsługi zdarzeń, 298, 301 programowanie obiektowe, 462, 473 projektowanie obiektowe, 462 proporcjonalna zmiana wielkości obrazu, 129 prototype, 468, 471, 475, 480, 481 Prototype, 604 prototypy, 468 przechowywanie danych, 61, 62, 151, 556 ciasteczka, 142 przechowywanie kodu JavaScript w zewnętrznych plikach, 145, 151 przeciążanie metod, 463 przeglądanie elementy drzewa DOM, 575 zawartość tablicy dwuwymiarowej, 257 przeglądarki WWW, 42, 111, 127 przekazywanie danych do skryptu PHP, 590 przekazywanie informacji do funkcji, 272 przeniesienie kodu skryptu do odrębnego pliku, 151 przepływ sterowania, 197 przerwanie działania licznika czasu, 119 przerwanie działania pętli, 234 przesyłanie danych na serwer, 332, 593 przesyłanie żądań Ajax, 569 przeszukiwanie łańcuch znaków, 441 tablica blogu, 442 wpisy w blogu, 439 przetwarzanie kodu HTML, 42 przyciski, 392, 581, 582 dezaktywacja, 582 efekt podświetlenia, 390 przypisanie metody do klasy, 467 pseudokod, 182<br /> <br /> Skorowidz push(), 256 puste miejsca, 190 pusty łańcuch znaków, 187 pusty przycisk, 392<br /> <br /> R<br /> <br /> random(), 448, 450 raportowanie błędów, 510 readCookie(), 143, 144 readyState, 560 referencja do funkcji, 293 refresh(), 119 RegExp, 344 rejestracja historii, 399 reload(), 123 removeChild(), 377 responseText, 560 return, 282, 283, 284, 289 round(), 448, 451 rozpoznawanie użytkownika, 136 rozszerzanie obiektów, 481 rozszerzenia kodu, 290 rozwiązywanie dużych problemów, 264 równość, 187 różny od, 187<br /> <br /> S<br /> <br /> Safari, 499 script, 41 sekcja nagłówka, 532, 533 send(), 560, 564, 567 separacja funkcjonalności od zawartości, 291 serwer, 112 setInterval(), 123, 130 setTimeout(), 119, 120, 121, 122, 130 shadow variable, 540 size, 327 skalowanie obrazka, 126 składanie operacji, 172 składowe, 410 skrypty, 35, 62, 111 cykl życia, 137<br /> <br /> skrypty PHP, 587 przekazywanie danych, 590 słowa kluczowe, 72 break, 202, 203, 234, 235 case, 201 const, 74, 77 continue, 234 else, 166 for, 213 function, 267 if, 162 new, 416 return, 282, 284 switch, 201, 202, 204 this, 318, 327, 417, 419, 466 var, 72, 77 while, 244 sort(), 434, 435, 436, 446 sortowanie, 423, 432, 436 tablice, 435 span, 389 split(), 144 sposoby usuwania błędów, 501 sprawdzanie poprawność danych, 101, 313, 315, 323 wartość zmiennej, 517 wypełnienie pola formularza, 100 src, 145, 226 stałe, 68, 74, 77 inicjalizacja, 88 nazwy, 74 stan żądania, 580 status, 560 status żądania, 580 statyczna strona WWW, 33 sterowanie przebiegiem akcji, 173 sterowanie przebiegiem zdarzeń, 170 sterowany danymi, 547 String, 427, 440 charAt(), 446 indexOf(), 441, 446 strona sterowna danymi, 547<br /> <br /> jesteś tutaj  613<br /> <br /> Skorowidz strona WWW, 33 struktura, 36 struktura strony HTML, 303 strukturalne rozszerzenia kodu, 290 struktury danych, 254, 409, 413 style, 36, 128, 393, 395 style CSS, 388 podmiana, 389 style.height, 128 style.width, 128 submit(), 332 switch, 201, 202, 204 break, 202 case, 201, 203 symulowanie ciasteczek, 155 syntax error, 510 szerokość okna klienta, 126, 127 szerokość okna przeglądarki, 126<br /> <br /> Ś<br /> <br /> średnik, 49, 167, 173<br /> <br /> T<br /> <br /> tablice, 220, 222, 224, 434 dodawanie elementów, 256 indeks, 220 indeksowane, 220 klucze, 220, 221 liczba elementów, 434 przeszukiwanie, 442 sortowanie, 434 tworzenie, 220 wartości, 220, 221 wielowymiarowe, 256 tablice dwuwymiarowe, 253, 254 dostęp do elementów, 255 klucze, 255 kolumny, 255 przeglądanie zawartości, 257 tworzenie, 254 wiersze, 255 tablice tablic, 254<br /> <br /> 614<br /> <br /> Skorowidz<br /> <br /> technologia skryptowa działająca po stronie serwera, 587 tekst, 63, 90, 427 tekstowa reprezentacja obiektu, 430 test(), 344 testowanie, 501 przy użyciu okienka dialogowego, 517, 519 w różnych przeglądarkach, 498 testy, 497 TEXT, 370 text/javascript, 41 this, 318, 327, 417, 419, 463, 466 timers, 118 toLowerCase(), 440, 446 toString(), 430, 437, 446 toUpperCase(), 440, 446 TPP, 496 treść dokumentu, 127 true, 63, 174, 188, 219 trwałe przechowywanie danych, 142 trwałość danych, 138 tworzenie drzewo decyzyjne, 176 elementy HTML, 399 formularze, 309 funkcje, 267 funkcje anonimowe, 299 jednokrotny licznik czasu, 130 kod HTML, 400 komentarze, 191 konsola do testowania skryptów, 527 liczniki czasu, 120 metody, 455 metody klasowe, 483 obiekt AjaxRequest, 571 obiekty, 220, 415, 416, 419, 462 przestrzeń na stronie, 362 tablice, 220 właściwości klasowe, 475 zmienne, 72 tymczasowe zmienne, 139 type, 41, 56 typy danych, 63, 77<br /> <br /> Skorowidz<br /> <br /> U<br /> <br /> undefined, 507 unikanie powielania kodu, 599 uogólnienie operacji, 274, 279 upraszczanie skryptu, 537 URL, 593 uruchamianie kod o określonej godzinie, 123 skrypty PHP, 589 ustawianie zawartości elementu HTML, 365 usuwanie błędy, 501 ciasteczka, 144 kod, 537 powtórzenia kodu, 274 użytkownicy, 32<br /> <br /> V<br /> <br /> value, 96 var, 72, 77 VBScript, 41<br /> <br /> W<br /> <br /> wartości atrybuty, 553 elementy stron WWW, 104 logiczne, 63, 90, 188, 219 NaN, 90 null, 507 tablica, 221 zmienne, 68 warunek zakończenia pętli, 233 warunki logiczne, 162 warunkowe wykonanie kodu, 162 wcięcia kodu, 163, 174 weryfikacja błędów składniowych, 510 weryfikacja danych, 100, 321 adres e-mail, 352, 355 daty, 333, 334 długość danych, 324 formularze, 312 kod pocztowy, 329<br /> <br /> numer telefonu, 351 wyrażenia regularne, 344 węzły, 369, 370 właściwości, 373 while, 244, 245, 248, 249 width, 128 wielkość okna przeglądarki, 126 wielokrotne stosowanie kodu, 51, 213, 274 window.onload, 298 właściwości, 410, 415 właściwości CSS, 395 właściwości klasowe, 473, 474, 478 tworzenie, 475 właściwości składowe, 466, 478 właściwości węzłów, 373 wpisy w blogu, 422 wprowadzanie informacji w polach formularza, 313 writeCookie(), 143, 144, 148 wskaźnik myszy, 391 WWW, 31 wybór opcji, 199 wydajność, 275 wykonywanie kodu JavaScript, 43, 114 wykonywanie kodu w momencie wczytywania strony, 533 wykonywanie skryptu, 43 wykrywanie przeglądarka, 155 pusty łańcuch znaków, 347 zmiana wielkości okna przeglądarki, 134 wykrywanie błędów, 500 błędy składniowe, 510 wyliczanie czasu, 425 wymiary obrazka, 128 wymiary okna klienta, 130 wyrażenia, 188 wyrażenia regularne, 336, 338, 343 $, 338 ^, 338 {}, 341, 347 +, 341 adres e-mail, 352 \d, 338<br /> <br /> jesteś tutaj  615<br /> <br /> Skorowidz wyrażenia regularne dopasowywanie określonej liczby powtórzeń, 347 dopasowywanie opcjonalnych znaków ze zbioru, 354 grupowanie podwyrażeń, 340 klasy znaków, 354 kwantyfikatory, 340 metaznaki, 338, 339 nawiasy, 340 numery telefonów, 351 powtórzenia, 341 RegExp, 344 \s, 338 test(), 344 \w, 338 weryfikacja danych, 342, 344 wzorce, 337 znaki ukośnika, 338 wyszukiwanie, 438, 445 wyświetlanie komunikaty o błędach, 315 obrazki, 491 puste komunikaty, 186 wywołanie funkcje, 293 metody klasowe, 486 wzorce, 336<br /> <br /> X<br /> <br /> XHTML, 553, 554 XML, 549, 550, 554, 555 XMLHttpRequest, 560, 561, 568 abort(), 560 implementacja w przeglądarce, 561 onreadystatechange, 560 open(), 560 readyState, 560 responseText, 560 send(), 560 status, 560 żądania asynchroniczne, 561<br /> <br /> 616<br /> <br /> Skorowidz<br /> <br /> Z<br /> <br /> zagnieżdżanie pętli, 249 zagnieżdżona instrukcja if, 179 zakres zmiennej, 192 zamiana funkcji na metodę, 455 zapamiętywanie informacji o użytkowniku, 62 zapis CamelCase, 90 zapisywanie ciasteczka, 148 pliki na serwerze, 586, 588 zasięg funkcyjny, 197 zastępowanie węzłów tekstowych, 384 zawartość, 291 zdarzenia, 48, 51, 297 funkcje anonimowe, 299 funkcje zwrotne, 297 onblur, 313, 314, 316, 325 onchange, 313, 316 onclick, 48 onfocus, 313 onload, 51, 298 onmouseout, 391 onmouseover, 391 onresize, 133 procedury obsługi, 298 złożone decyzje, 240 złożone struktury danych, 256 zmiana stan żądania, 560 szerokość okna przeglądarki, 126 wielkość obrazka, 128 wielkość okna, 133 zawartość węzłów tekstowych, 377 zmienne, 68, 77, 140, 170, 505 cykl życia, 193 globalne, 193, 196, 412 inicjalizacja, 73, 77 kontekst, 193 lokalizacja, 192<br /> <br /> Skorowidz lokalne, 193, 196 nazwy, 72 niezdefiniowane, 507 określanie typu danych, 73 sprawdzanie wartości, 517 tworzenie, 72 tymczasowe, 139 wartości, 68 zakres, 192 zmienne-cień, 540 znaki specjalne, 78<br /> <br /> znaki unikowe, 513, 521 zwracanie wartości z funkcji, 269, 282<br /> <br /> Ż<br /> <br /> żądania, 112, 113, 558 Ajax, 567 asynchroniczne, 561, 568, 572 GET, 562, 567 HTTP, 569 POST, 562, 593<br /> <br /> jesteś tutaj  617<br /> <br /> </div> </div> </div> <div class="row hidden-xs"> <div class="col-md-12"> <h2></h2> <hr /> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/griffiths-d-head-first-statystyka-edycja-polska.html"> <img src="https://docer.tips/img/300x300/griffiths-d-head-first-statystyka-edycja-polska_5a578464d64ab2c3046ad2d6.jpg" alt="Griffiths D. - Head First. Statystyka. Edycja polska" /> <h3 class="note-title">Griffiths D. - Head First. Statystyka. Edycja polska</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/griffiths-d-head-first-statystyka-edycja-polska.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/pilone-t-head-first-algebra-edycja-polska.html"> <img src="https://docer.tips/img/300x300/pilone-t-head-first-algebra-edycja-polska_5a604a69d64ab2ea20477865.jpg" alt="Pilone T. - Head First Algebra. Edycja polska" /> <h3 class="note-title">Pilone T. - Head First Algebra. Edycja polska</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/pilone-t-head-first-algebra-edycja-polska.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/lang-h-head-first-fizyka-edycja-polska.html"> <img src="https://docer.tips/img/300x300/lang-h-head-first-fizyka-edycja-polska_5a89960dd64ab25a230bfba2.jpg" alt="Lang H. - Head First. Fizyka. Edycja polska" /> <h3 class="note-title">Lang H. - Head First. Fizyka. Edycja polska</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/lang-h-head-first-fizyka-edycja-polska.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/lang-h-head-first-fizyka-edycja-polska-pdf.html"> <img src="https://docer.tips/img/300x300/lang-h-head-first-fizyka-edycja-polska_5abd9891d64ab2116d46ed6b.jpg" alt="Lang H Head First Fizyka Edycja polska" /> <h3 class="note-title">Lang H Head First Fizyka Edycja polska</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/lang-h-head-first-fizyka-edycja-polska-pdf.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-javascript-programming.html"> <img src="https://docer.tips/img/300x300/head-first-javascript-programming_5a898930d64ab26f6272c4ad.jpg" alt="Head First JavaScript Programming" /> <h3 class="note-title">Head First JavaScript Programming</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-javascript-programming.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2007-head-first-javascript.html"> <img src="https://docer.tips/img/300x300/2007-head-first-javascript_5a898d46d64ab26e62d63b3f.jpg" alt="2007-Head First JavaScript" /> <h3 class="note-title">2007-Head First JavaScript</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2007-head-first-javascript.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-javascript.html"> <img src="https://docer.tips/img/300x300/head-first-javascript_5a898a7bd64ab26e62d63b3e.jpg" alt="Head First JavaScript" /> <h3 class="note-title">Head First JavaScript</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-javascript.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2007-head-first-javascript-short.html"> <img src="https://docer.tips/img/300x300/2007-head-first-javascript-short_5a899412d64ab25b237009c8.jpg" alt="2007-Head First Javascript (short)" /> <h3 class="note-title">2007-Head First Javascript (short)</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2007-head-first-javascript-short.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/basham-b-head-first-servlets-jsp-edycja-polska-wydanie-ii.html"> <img src="https://docer.tips/img/300x300/basham-b-head-first-servlets-jsp-edycja-polska-wyd_5a8225ecd64ab2859670b15b.jpg" alt="Basham B. - Head First Servlets & JSP. Edycja polska. Wydanie II" /> <h3 class="note-title">Basham B. - Head First Servlets & JSP. Edycja polska. Wydanie II</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/basham-b-head-first-servlets-jsp-edycja-polska-wydanie-ii.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/watrall-e-head-first-web-design-edycja-polska.html"> <img src="https://docer.tips/img/300x300/watrall-e-head-first-web-design-edycja-polska_5a3b922bb7d7bc2e7ca32539.jpg" alt="Watrall E. - Head First Web Design. Edycja polska" /> <h3 class="note-title">Watrall E. - Head First Web Design. Edycja polska</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/watrall-e-head-first-web-design-edycja-polska.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/britanica-edycja-polska-tom.html"> <img src="https://docer.tips/img/300x300/britanica-edycja-polska-tom_5a75acdcd64ab239711ef3b7.jpg" alt="Britanica Edycja Polska TOM" /> <h3 class="note-title">Britanica Edycja Polska TOM</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/britanica-edycja-polska-tom.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-javascript-programming-eric-freeman-elisabeth-rob.html"> <img src="https://docer.tips/img/300x300/head-first-javascript-programming-eric-freeman-eli_5a899c5fd64ab25d23e3397e.jpg" alt="Head First JavaScript Programming - Eric Freeman, Elisabeth Robson" /> <h3 class="note-title">Head First JavaScript Programming - Eric Freeman, Elisabeth Robson</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-javascript-programming-eric-freeman-elisabeth-rob.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-sieci-komputerowe-edycja-polska-al-anderson-ryan-.html"> <img src="https://docer.tips/img/300x300/head-first-sieci-komputerowe-edycja-polska-al-ande_5a89a4b6d64ab2b9ca660aaf.jpg" alt="Head First. Sieci komputerowe. Edycja polska - Al Anderson, Ryan Benedetti [HQ]" /> <h3 class="note-title">Head First. Sieci komputerowe. Edycja polska - Al Anderson, Ryan Benedetti [HQ]</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-sieci-komputerowe-edycja-polska-al-anderson-ryan-.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-mobile-web.html"> <img src="https://docer.tips/img/300x300/head-first-mobile-web_5a8987ddd64ab26f6272c4ac.jpg" alt="Head First Mobile Web" /> <h3 class="note-title">Head First Mobile Web</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-mobile-web.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2008-head-first-statistics.html"> <img src="https://docer.tips/img/300x300/2008-head-first-statistics_5a8980f1d64ab24dcbdc855e.jpg" alt="2008-Head First Statistics" /> <h3 class="note-title">2008-Head First Statistics</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2008-head-first-statistics.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2008-head-first-physics.html"> <img src="https://docer.tips/img/300x300/2008-head-first-physics_5a898601d64ab26f6272c4ab.jpg" alt="2008-Head First Physics" /> <h3 class="note-title">2008-Head First Physics</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2008-head-first-physics.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2010-head-first-excel.html"> <img src="https://docer.tips/img/300x300/2010-head-first-excel_5a7f2029d64ab2d88f97e784.jpg" alt="2010-Head First Excel" /> <h3 class="note-title">2010-Head First Excel</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2010-head-first-excel.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-c.html"> <img src="https://docer.tips/img/300x300/head-first-c_5a589594d64ab2dd5d4cbe61.jpg" alt="Head First C" /> <h3 class="note-title">Head First C</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-c.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2012-head-first-c.html"> <img src="https://docer.tips/img/300x300/2012-head-first-c_5a58a0b2d64ab2de5d4c3f49.jpg" alt="2012-Head First C" /> <h3 class="note-title">2012-Head First C</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2012-head-first-c.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2008-head-first-ajax.html"> <img src="https://docer.tips/img/300x300/2008-head-first-ajax_5a897f53d64ab24dcbdc855d.jpg" alt="2008-Head First Ajax" /> <h3 class="note-title">2008-Head First Ajax</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2008-head-first-ajax.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2010-head-first-python.html"> <img src="https://docer.tips/img/300x300/2010-head-first-python_5a898130d64ab24ccb922104.jpg" alt="2010-Head First Python" /> <h3 class="note-title">2010-Head First Python</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2010-head-first-python.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-rails-mniejszy.html"> <img src="https://docer.tips/img/300x300/head-first-rails-mniejszy_5a8983bbd64ab24dcbdc8560.jpg" alt="Head First Rails (mniejszy)" /> <h3 class="note-title">Head First Rails (mniejszy)</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-rails-mniejszy.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2009-head-first-programming.html"> <img src="https://docer.tips/img/300x300/2009-head-first-programming_5a898446d64ab24bcbf5c67a.jpg" alt="2009-Head First Programming" /> <h3 class="note-title">2009-Head First Programming</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2009-head-first-programming.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2011-head-first-jquery.html"> <img src="https://docer.tips/img/300x300/2011-head-first-jquery_5a898784d64ab26e62d63b3c.jpg" alt="2011-Head First jQuery" /> <h3 class="note-title">2011-Head First jQuery</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2011-head-first-jquery.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2008-head-first-algebra.html"> <img src="https://docer.tips/img/300x300/2008-head-first-algebra_5a603e16d64ab23f505e5093.jpg" alt="2008-Head First Algebra" /> <h3 class="note-title">2008-Head First Algebra</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2008-head-first-algebra.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2010-head-first-wordpress.html"> <img src="https://docer.tips/img/300x300/2010-head-first-wordpress_5a898723d64ab270626edcd2.jpg" alt="2010-Head First WordPress" /> <h3 class="note-title">2010-Head First WordPress</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2010-head-first-wordpress.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-python-mniejszy.html"> <img src="https://docer.tips/img/300x300/head-first-python-mniejszy_5a898819d64ab26e62d63b3d.jpg" alt="Head First Python (mniejszy)" /> <h3 class="note-title">Head First Python (mniejszy)</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-python-mniejszy.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/2003-head-first-ejb.html"> <img src="https://docer.tips/img/300x300/2003-head-first-ejb_5a8989b5d64ab26f6272c4ae.jpg" alt="2003-Head First EJB" /> <h3 class="note-title">2003-Head First EJB</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/2003-head-first-ejb.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-pmp-preparation.html"> <img src="https://docer.tips/img/300x300/head-first-pmp-preparation_5a898b4ad64ab270626edcd3.jpg" alt="Head First PMP Preparation" /> <h3 class="note-title">Head First PMP Preparation</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-pmp-preparation.html">Read more</a> </div> </div> </div> <div class="col-lg-2 col-md-3"> <div class="note"> <div class="note-meta-thumb"> <a href="https://docer.tips/head-first-sql.html"> <img src="https://docer.tips/img/300x300/head-first-sql_5a86987ad64ab215f1e3cd2b.jpg" alt="Head First SQL" /> <h3 class="note-title">Head First SQL</h3> </a> </div> <div class="note-action"> <a class="more-link" href="https://docer.tips/head-first-sql.html">Read more</a> </div> </div> </div> </div> </div> <div class="col-lg-3 col-md-4 col-xs-12"> <div class="panel-recommend panel panel-primary"> <div class="panel-heading"> <h4 class="panel-title">Recommend Documents</h4> </div> <div class="panel-body"> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/griffiths-d-head-first-statystyka-edycja-polska.html"> <img src="https://docer.tips/img/60x80/griffiths-d-head-first-statystyka-edycja-polska_5a578464d64ab2c3046ad2d6.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/griffiths-d-head-first-statystyka-edycja-polska.html"> Griffiths D. - Head First. Statystyka. Edycja polska </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/pilone-t-head-first-algebra-edycja-polska.html"> <img src="https://docer.tips/img/60x80/pilone-t-head-first-algebra-edycja-polska_5a604a69d64ab2ea20477865.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/pilone-t-head-first-algebra-edycja-polska.html"> Pilone T. - Head First Algebra. Edycja polska </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/lang-h-head-first-fizyka-edycja-polska.html"> <img src="https://docer.tips/img/60x80/lang-h-head-first-fizyka-edycja-polska_5a89960dd64ab25a230bfba2.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/lang-h-head-first-fizyka-edycja-polska.html"> Lang H. - Head First. Fizyka. Edycja polska </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/lang-h-head-first-fizyka-edycja-polska-pdf.html"> <img src="https://docer.tips/img/60x80/lang-h-head-first-fizyka-edycja-polska_5abd9891d64ab2116d46ed6b.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/lang-h-head-first-fizyka-edycja-polska-pdf.html"> Lang H Head First Fizyka Edycja polska </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/head-first-javascript-programming.html"> <img src="https://docer.tips/img/60x80/head-first-javascript-programming_5a898930d64ab26f6272c4ad.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/head-first-javascript-programming.html"> Head First JavaScript Programming </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/2007-head-first-javascript.html"> <img src="https://docer.tips/img/60x80/2007-head-first-javascript_5a898d46d64ab26e62d63b3f.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/2007-head-first-javascript.html"> 2007-Head First JavaScript </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/head-first-javascript.html"> <img src="https://docer.tips/img/60x80/head-first-javascript_5a898a7bd64ab26e62d63b3e.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/head-first-javascript.html"> Head First JavaScript </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/2007-head-first-javascript-short.html"> <img src="https://docer.tips/img/60x80/2007-head-first-javascript-short_5a899412d64ab25b237009c8.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/2007-head-first-javascript-short.html"> 2007-Head First Javascript (short) </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/basham-b-head-first-servlets-jsp-edycja-polska-wydanie-ii.html"> <img src="https://docer.tips/img/60x80/basham-b-head-first-servlets-jsp-edycja-polska-wyd_5a8225ecd64ab2859670b15b.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/basham-b-head-first-servlets-jsp-edycja-polska-wydanie-ii.html"> Basham B. - Head First Servlets & JSP. Edycja polska. Wydanie II </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> <div class="row m-0"> <div class="col-md-3 col-xs-3 pl-0 text-center"> <a href="https://docer.tips/watrall-e-head-first-web-design-edycja-polska.html"> <img src="https://docer.tips/img/60x80/watrall-e-head-first-web-design-edycja-polska_5a3b922bb7d7bc2e7ca32539.jpg" alt="" width="100%" /> </a> </div> <div class="col-md-9 col-xs-9 p-0"> <label> <a href="https://docer.tips/watrall-e-head-first-web-design-edycja-polska.html"> Watrall E. - Head First Web Design. Edycja polska </a> </label> <div class="note-meta"> <div class="note-desc"></div> </div> </div> <div class="clearfix"></div> <hr class="mt-15 mb-15" /> </div> </div> </div> </div> </div> </div> <script> $(document).ready(function () { var inner_height = $(window).innerHeight() - 250; $('#pdfviewer').css({"height": inner_height + "px"}); }); </script> <footer class="footer" style="margin-top: 60px;"> <div class="container-fluid"> Copyright © 2024 DOCER.TIPS. All rights reserved. <div class="pull-right"> <span><a href="https://docer.tips/about">About Us</a></span> | <span><a href="https://docer.tips/privacy">Privacy Policy</a></span> | <span><a href="https://docer.tips/term">Terms of Service</a></span> | <span><a href="https://docer.tips/copyright">Copyright</a></span> | <span><a href="https://docer.tips/contact">Contact Us</a></span> </div> </div> </footer> <!-- Modal --> <div class="modal fade" id="login" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close" on="tap:login.close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="add-note-label">Sign In</h4> </div> <div class="modal-body"> <form action="https://docer.tips/login" method="post"> <div class="form-group"> <label class="sr-only" for="email">Email</label> <input class="form-input form-control" type="text" name="email" id="email" value="" placeholder="Email" /> </div> <div class="form-group"> <label class="sr-only" for="password">Password</label> <input class="form-input form-control" type="password" name="password" id="password" value="" placeholder="Password" /> </div> <div class="form-group"> <div class="checkbox"> <label class="form-checkbox"> <input type="checkbox" name="remember" value="1" /> <i class="form-icon"></i> Remember me </label> <label class="pull-right"><a href="https://docer.tips/forgot">Forgot password?</a></label> </div> </div> <button class="btn btn-primary btn-block" type="submit">Sign In</button> </form> <hr style="margin-top: 15px;" /> <a href="https://docer.tips/login/facebook" class="btn btn-facebook btn-block"><i class="fa fa-facebook"></i> Login with Facebook</a> <a href="https://docer.tips/login/google" class="btn btn-google btn-block"><i class="fa fa-google"></i> Login with Google</a> </div> </div> </div> </div> <!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-111599147-1"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-111599147-1'); </script> <script src="https://docer.tips/assets/js/jquery-ui.min.js"></script> <link rel="stylesheet" href="https://docer.tips/assets/css/jquery-ui.css"> <script> $(function () { $("#document_search").autocomplete({ source: function (request, response) { $.ajax({ url: "https://docer.tips/suggest", dataType: "json", data: { term: request.term }, success: function (data) { response(data); } }); }, autoFill: true, select: function( event, ui ) { $(this).val(ui.item.value); $(this).parents("form").submit(); } }); }); </script> </html>