W dobie aplikacji Web 2.0 technologia Ajax robi zawrotną karierę. Pierwszy odcinek kursu Ajaksa wprowadzi w świat interaktywnych aplikacji WWW. Omawiany przykład prezentuje wszystkie cechy aplikacji ajaksowych: interaktywną wymianę treści i wyglądu przy użyciu modelu DOM, modyfikację fragmentu strony WWW bez przeładowywania całego dokumentu, asynchroniczną komunikację z serwerem prowadzoną w tle oraz operowanie danymi w formacie XML.
Ajax na stronach WWW
Strona WWW stosująca technologię Ajax zawiera elementy dynamiczne. Pod wpływem interakcji użytkownika wygląd i treść witryny ulegają zmianie. Zasadniczą cechą wyróżniającą witryny ajaksowe jest brak przeładowania całej strony WWW. Zachowanie takie możemy zaobserwować m.in. w serwisie flickr (http://flickr.com).
Rysunek po lewej prezentuje zdjęcie zatytułowane Tatry… I LOVE YOU!!!. Po prawej stronie znajdują się kontrolki umożliwiające przeglądanie kategorii. Każda kategoria może zostać zwinięta (służy do tego ikona -) lub rozwinięta (ikona {stala}+{/stala}). Rysunek po prawej prezentuje tą samą witrynę po zwinięciu pierwszej kategorii.
Dynamicznym fragmentem strony przedstawionej na powyższych rysunkach jest zaznaczona na czerwono kontrolka wyświetlająca miniaturowe fotografie. Element ten jest widoczny (rozwinięty) lub ukryty (zwinięty ).
Inter akcja użytkownika polega na kliknięciu ikony {stala}+{/stala} lub {stala}-{/stala}. W odróżnieniu od zwykłych hiperłączy, strona nie zostaje przeładowana. Zmianie ulega tylko fragment witryny.
Zawartość rozwinięte go elementu pochodzi z serwer a (są to miniaturki innych fotografii należących do danej kategorii). Zatem po rozwinięciu elementu przeglądarka w tle pobiera z serwera dane i umieszcza je w odpowiednim obszar ze strony WWW.
Ajax, czyli asynchroniczny JavaScript i XML
Strona wykorzystująca Ajax jest zwykłym dokumentem HTML /CSS zawierającym skrypty JavaScript. Ajax nie w prowadza żadnych nowych języków. Interakcje użytkownik a (np. kliknięcie ikony, wskazanie elementu kursorem myszki) jest realizowane poprzez zdarzenia zdefiniowane w specyfikacji HTML (m.in. {stala}onclick{/stala}, {stala}onmouseover{/stala}, {stala}onmouseout{/stala}). Może to być kliknięcie elementu {stala}span{/stala} czy wskazanie kursorem myszki obrazka img. Cała dynamiczna interakcja jest oprogramowana w języku JavaScript.
Wysyłanie w tle zapytań HTTP do serwera o dodatkowe dane odbywa się przy użyciu obiektu JavaScript o nazwie {stala}XMLHttpRequest{/stala}. Po odebraniu danych z serwera modyfikujemy stronę WWW, wykorzystując do tego model DOM. Innymi słowy w skrypcie JavaScript wywołujemy metody (np. {stala}getElementById(){/stala}, {stala}getElementsByTagName(){/stala} ), by uzyskać dostęp do poszczególnych elementów HTML strony, która jest właśnie wyświetlona przez przeglądarkę. Różnymi właściwościami (np. {stala}innerHTML{/stala}, style) modyfikujemy treść ( np. w stawiamy miniaturk i pobrane w tle z serwera ) i wygląd poszczególnych elementów HTML ( np. rozwijamy/zwijamy element div ).
Podsumowując, tworzenie ajaksowych stron WWW wymaga znajomości:
- języków HTML /CSS, w szczególności zdarzeń HTML,
- języka JavaScript,
- obiektu {stala}XMLHttpRequest{/stala} (jest to obiekt dostępny w JavaScript),
- protokołu HTTP, w szczególności metod GET o raz POST,
- języka XML ,
- modelu DOM (czyli obiektów, ich metod oraz właściwości dostępnych w JavaScripcie, które pozwalają na modyfikowanie strony WWW wyświetlanej przez przeglądarkę).
Języki przetwarzania po stronie serwera (takie jak PHP, ASP czy JSP) nie są konieczne. W pierwszym odcinku kursu wszystkie przykłady będą napisane wyłącz nie w językach XHTML, CSS, JavaScript, XML, bez przetwarzania po stronie serwera.
Rozwiązaniami podobnymi do Ajaksa są AHAH ( Asynchronous HTML and HTTP – asynchroniczny HT ML i HT TP) oraz AXAH (Asynchronous XHTML and HTTP – asynchroniczny XHTML i HTTP). Różnią się one od Ajaksa formatem danych. Obecnie, bez względu na format danych, rozwiązania stosujące asynchroniczną komunikację z serwerem WWW są określane terminem Ajax (nawet, jeśli nie stosują XML).
Praktyczne poznawanie Ajaksa rozpoczniemy od tworzenia obiektu do komunikacji asynchronicznej. W przeglądarkach Firefox, Opera oraz Internet Explorer 7 obiekt taki tworzymy następująco:
/* Przeglądarki: Firefox 2, Opera 9, IE 7 */
request = new XMLHttpRequest();
IE6 wymaga kodu:
/* Przeglądarka: IE 6 */
request = new ActiveXObject(\'Msxml2.XMLHTTP\');
Zaś w IE5 obiekt request tworzymy tak:
/* Przeglądarka: IE 5 */
request = new ActiveXObject(\'Microsoft.XMLHTTP\');
Uniwersalna procedura tworzenia obiektu {stala}XMLHttpRequest{/stala}:
function getXMLHttpRequest() {
var request = false;
try {
request = new XMLHttpRequest();
} catch(err1) {
try {
request = new ActiveXObject(\'Msxml2.XMLHTTP\');
} catch(err2) {
try {
request = new ActiveXObject(\'Microsoft.XMLHTTP\');
} catch(err3) {
request = false;
}
}
}
return request;
}
Będzie ona działała poprawnie w większości współczesnych przeglądarek. Wynikiem funkcji {stala}getXMLHttpRequest(){/stala} jest utworzony obiekt lub – w przypadku niepowodzenia – wartość {stala}false{/stala}. Z funkcji tej korzystamy następująco:
var r;
r = getXMLHttpRequest();
Obiekt r jest gotowy do wysyłania zapytań protokołem HTTP do serwera WWW. Powyższe dwa kody są napisane w języku JavaScript. Należy je umieścić w nagłówku strony WWW:
...
Odbieranie danych w formacie tekstowym
Przejdźmy do wykonania kompletnego przykładu demonstrującego wysyłanie żądań HTTP i odbieranie wyników w formacie tekstowym. Takie rozwiązania są określane mianem AHAH. Rozwiązanie składa się z dwóch plików: {stala}index.html{/stala} oraz {stala}dane.txt{/stala}. Plik {stala}dane.txt{/stala} zawiera jedną linijkę:
Lorem ipsum...
Poniższy kod przedstawia zarys strony {stala}index.html{/stala}. Skrypt JavaScript zawarty w nagłówku strony rozpoczyna się od definicji funkcji {stala}get-XMLHttpRequest(){/stala}. Następnie funkcja ta jest wywołana, a zwrócony przez nią obiekt przypisany do zmiennej r. Kolejnym elementem jest definicja funkcji {stala}processResponse(){/stala}. Funkcja ta będzie wywołana po zakończeniu transmisji danych z serwera. W jej treści odwołujemy się do zmiennej globalnej r – tj. utworzonego wcześniej obiektu {stala}XMLHttpRequest{/stala}. Skrypt kończymy wywołując metody {stala}open(){/stala}, {stala}send(){/stala} oraz przypisując funkcję {stala}processResponse(){/stala} do obsługi zdarzenia {stala}onreadystatechange{/stala}.
...
Fragmentem odpowiedzialnym za wyświetlenie danych pochodzących z serwera jest wiersz:
alert(\'Tekst z serwera: \' + r.responseText);
Dane (w formacie tekstowym) pochodzące z serwera (tj. z pliku {stala}dane.txt{/stala}) są dostępne we właściwości {stala}responseText{/stala} obiektu {stala}XMLHttpRequest{/stala}. Tak wykonana strona nie wykorzystuje asynchroniczności transferu: dane wysyłane przez serwer zostaną wyświetlone (w okienku informacyjnym {stala}alert(){/stala}) natychmiast po odwiedzeniu strony {stala}index.html{/stala}.
Opisany przykład wykorzystuje protokół HTTP. Nie można go więc uruchomić w wersji offline (np. z płyty CD). W celu uruchomienia przykładu trzeba dysponować zainstalowanym serwerem WWW (np. Apache). Po przekopiowaniu plików do folderu {stala}htdocs/{/stala}, który jest przeznaczony na strony WWW, przykład uruchamiamy odwiedzając stronę {stala}http://localhost{/stala}.
Odbieranie danych w formacie XML
Strona prezentująca wymianę danych w formacie XML w głównym zarysie wygląda podobnie do strony stosującej surowy tekst. Składa się z dwóch plików: {stala}index.html{/stala} oraz {stala}dane.xml{/stala}. Użycie języka XML do transferu danych wymaga wymiany dwóch elementów. Po pierwsze plik danych {stala}dane.xml{/stala} zawiera XML:
Lorem ipsum...
Po drugie dane XML odebrane z serwera należy przed wyświetleniem przetworzyć.
Dostęp do danych w formacie XML zapewnia właściwość {stala}responseXML{/stala} obiektu {stala}XMLHttpRequest{/stala}. Do przetworzenia kodu XML służą m.in.: metoda {stala}getElementsByTagName(){/stala} i właściwości {stala}childNodes{/stala} oraz {stala}nodeValue{/stala}.
Wewnątrz funkcji {stala}processResponse(){/stala} najpierw odbieramy XML zwrócony przez serwer:
var x1 = r.responseXML;
W następnym kroku wyszukujemy element XML o nazwie tekst:
var x2 = x1.getElementsByTagName(\'tekst\');
po czym pobieramy pierwszy ze znalezionych elementów <tekst>:
var x3 = x2[0];
Teraz przechodzimy do potomków, tj. elementów zawartych wewnątrz elementu {stala}<tekst>…</tekst>{/stala}:
var x4 = x3.childNodes;
i pobieramy pierwszego z nich:
var x5 = x4[0];
Wartość potomka umieszczamy w zmiennej x6:
var x6 = x5.nodeValue;
i wyświetlamy w oknie informacyjnym:
alert(\'XML z serwera: \' + x6);
Całość możemy wykonać jedną instrukcją:
alert(\'XML z serwera: \' + r.responseXML.getElementsByTagName(\'tekst\') .[0].childNodes[0].nodeValue);
Zarys strony {stala}index.html{/stala} pobierającej z serwera dane w formacie XML:
Witryna wykonana w technologii Ajax składa się z dwóch komponentów: dokumentu HTML oraz danych udostępnianych przez serwer WWW. Przeglądarka w odpowiedzi na interakcje użytkownika (np. kliknięcie ikony {stala}+{/stala}) wysyła zapytanie HTTP do serwera. W odpowiedzi serwer przekazuje do strony WWW (dokładniej: do skryptu JavaScript zawartego w dokumencie HTML) dane. W tym kroku skupimy się na wysłaniu żądania oraz odebraniu wyników.
Do wysyłania żądań HTTP obiekt {stala}XMLHttp-Request{/stala} ma metody {stala}open(){/stala} oraz {stala}send(){/stala}. Metoda {stala}open(){/stala} przygotowuje zapytanie HTTP, a {stala}send(){/stala} rozpoczyna transmisję.
Funkcja {stala}open(){/stala} ma trzy parametry: pierwszym jest nazwa metody protokołu HTTP, drugim – adres URL danych, zaś trzecim – flaga logiczna, ustalająca czy żądanie ma być realizowane asynchronicznie (tj. w tle, bez czekania na zakończenie). Wywołanie:
r.open(\'GET\', \'dane.txt\', true);
przygotuje asynchroniczne żądanie GET dotyczące dokumentu dane.txt. Podany adres URL może dotyczyć nie tylko pliku tekstowego, ale pliku XML, PHP, ASP, JSP czy dowolnego innego zasobu dostępnego w ramach usługi WWW:
r.open(\'GET\', \'dane.xml\', true);
r.open(\'GET\', \'skrypt.php\', true);
r.open(\'GET\', \'strona.asp\', true);
r.open(\'GET\', \'witryna.jsp\', true);
r.open(\'GET\', \'plik.jpg\', true);
Może to również być pełny adres URL odnoszący się do innego serwera:
r.open(\'GET\', \'http://www.example.net/d/data.php\', true);
czy adres zawierający zmienne URL (tj. fragment występujący w adresie URL po znaku zapytania):
r.open(\'GET\', \'http://www.example.net/d/get.php?id=123\', true);
Żądanie przygotowane metodą {stala}open(){/stala} wysyłamy wywołując metodę {stala}send(){/stala}:
r.send(null);
Metoda {stala}send(){/stala} ma jeden parametr: dane dołączane do zapytania. Parametr ten należy wykorzystać w przypadku metody POST. Jeśli stosowaną metodą jest GET, wówczas metodę {stala}send(){/stala} wywołujemy podając parametr {stala}null{/stala}. W celu odebrania wyników zwracanych przez obiekt {stala}XMLHttpRequest{/stala} należy przygotować funkcję, która zostanie wywołana po zakończeniu transmisji. Funkcja ta może mieć dowolną nazwę, np. {stala}processResponse(){/stala}. Należy ją przypisać do obsługi zdarzenia {stala}onreadystatechange{/stala} obiektu {stala}XMLHttpRequest{/stala}:
r.onreadystatechange = processResponse;
W treści funkcji sprawdzamy czy nadeszła odpowiedź na wysłane żądanie oraz czy żądanie HTTP zostało poprawnie przetworzone przez serwer. Właściwość {stala}readyState{/stala} o wartości 4 informuje o tym, że nadeszła odpowiedź na wysłane żądanie, zaś właściwość status zawiera kod odpowiedzi HTTP. Wartość 200 oznacza, że serwer poprawnie przetworzył żądanie.
Funkcja odpowiedzialna za odbieranie danych z serwera przyjmuje postać:
function processResponse() {
if (r.readyState == 4) {
if (r.status == 200) {
...
}
}
}
Zwróćmy uwagę, że jest w niej wykorzystana zmienna globalna r. Jest to oczywiście obiekt {stala}XHMLHttpRequest{/stala}.
Zasadniczą cechą wyróżniającą strony stosujące Ajaksa jest asynchroniczność połączona z wymianą tylko fragmentu dokumentu. W wyniku akcji użytkownika (np. kliknięcia ikony {stala}+{/stala}) następuje wysłanie żądania do serwera, odebranie danych i umieszczenie nowej treści w wybranym miejscu strony. W celu oprogramowania takiego zachowania należy poznać technikę wymiany fragmentu strony WWW oraz reakcji na zdarzenia.
Model DOM i metoda getElementById()
Do manipulacji stroną WWW wyświetlaną przez przeglądarkę służy model DOM. Cała strona WWW widoczna w bieżącej chwili jest dostępna w skryptach JavaScript za pośrednictwem zestawu obiektów, metod i właściwości.
Jeśli na stronie WWW znajduje się element o identyfikatorze #tresc:
to dostęp do niego możemy uzyskać wywołując metodę {stala}getElementById(){/stala}. Jej parametrem jest identyfikator elementu HTML. Metoda ta zwraca obiekt:
var el;
el = document.getElementById(\'tresc\');
który możemy poddać manipulacjom. Możemy wymienić jego treść:
el.innerHTML = \'Lorem ipsum...\';
oraz styl CSS:
el.style.border = \'2px solid red\';
Do wymiany treści elementu służy właściwość {stala}innerHTML{/stala}, a do modyfikacji stylów – właściwość style. Poszczególne właściwości CSS są dostępne po kropce, przy czym znak {stala}-{/stala}zostaje zastąpiony znakiem {stala}_{/stala}:
el.style.margin_left = \'50px\';
Kod znajdujący się poniżej przedstawia przykładową stronę WWW, w której dynamicznie wymieniono treść i format elementu {stala}div{/stala}. Pomimo tego, że element {stala}div#tresc{/stala} jest pusty, jeśli odwiedzimy stronę {stala}index.html{/stala}, ujrzymy tekst {stala}Lorem ipsum{/stala} na czerwonym tle. Za wstawienie i sformatowanie tekstu odpowiada skrypt JavaScript umieszczony poniżej elementu {stala}div{/stala}. Przykład ten nie wykorzystuje protokołu HTTP. Może więc być uruchomiony offline.
Zdarzenia HTML
Interaktywne reakcje na zachowanie użytkownika są oprogramowane za pomocą zdarzeń HTML. Niemal każdy element HTML może być wzbogacony o zdarzenia:
...
...
Treścią obsługi zdarzenia jest funkcja JavaScript. Reakcją na kliknięcie elementu może być zmiana treści oraz formatu. Najpierw przygotowujemy element HTML, który będzie poddany zmianom po wystąpieniu zdarzenia:
Tekst tekst tekst...
Następnie przygotowujemy element HTML, który będzie generował zdarzenie. Kliknięcie poniższego elementu li, będzie powodowało wywołanie funkcji {stala}onlickHandler(){/stala}:
Funkcja {stala}onclickHandler(){/stala} odpowiada za zmianę treści oraz formatu elementu o identyfikatorze {stala}#tresc{/stala}:
function onclickHandler() {
var el;
el = document.getElementById(\'tresc\');
el.innerHTML = \'click click click...\';
el.style.border = \'2px solid red\';
}
Zarys przykładu prezentującego obsługę zdarzeń {stala}onclick{/stala}, {stala}onmouseover{/stala} oraz {stala}onmouseout{/stala}:
- onclick
- onmouseover
- onmouseout
Piosenki
Wykorzystując zdarzenie {stala}onmouseover{/stala} przygotujmy pierwszy przykład, który będzie demonstrował asynchroniczną wymianę fragmentu strony WWW. Strona będzie zawierała menu i treść. Pozycjami menu będą tytuły piosenek. Po wskazaniu tytułu piosenki wskaźnikiem myszy treść wybranej piosenki będzie umieszczana na stronie WWW. Całość będzie się odbywała asynchronicznie: tekst piosenki będzie pobierany z serwera (w formacie XML) dopiero po wskazaniu wybranej pozycji menu kursorem myszy.
Przykład składa się z czterech plików: dokumentu {stala}index.html{/stala} oraz trzech plików z tekstami piosenek {stala}krasnoludki.xml{/stala}, {stala}misie.xml{/stala}, {stala}lisek.xml{/stala}. Pliki z danymi są zawarte w folderze dane/ i mają identyczną strukturę. Oto fragment pliku {stala}krasnoludki.xml{/stala}:
Krasnoludki
My jesteśmy krasnoludki,
...
A tak wygląda zarys kodu HTML strony {stala}index.html{/stala}:
...
W menu znajdują się elementy li zawierające obsługę zdarzeń {stala}onmouseover{/stala} oraz {stala}onmouseout{/stala}:
Skrypt JavaScript rozpoczynamy od definicji funkcji {stala}getXMLHttpRequest(){/stala} i utworzenia obiektu {stala}r{/stala}. Następnie definiujemy funkcję {stala}processResponse(){/stala}, która będzie wywoływana po odebraniu danych z serwera. W treści tej funkcji sprawdzamy, czy żądanie zostało poprawnie przetworzone przez serwer ({stala}r.ready-State == 4{/stala} oraz {stala}r.status == 200{/stala}). Jeśli tak, to odebrany tekst w formacie XML (czyli {stala}r.responseXML{/stala}) wstawiamy do elementu HTML o identyfikatorze {stala}#tresc{/stala}. Dodatkowo zmieniamy kolor tła elementu {stala}div#tresc{/stala}:
function processResponse() {
if (r.readyState == 4) {
if (r.status == 200) {
document.getElementById(\'tresc\').innerHTML = r.responseXML.getElementsByTagName(\'tekst\') .[0].childNodes[0].nodeValue;
document.getElementById(\'tresc\'.style.background = \'#e3f5fb\';
};
}
}
Pobranie danych z serwera rozpoczyna się w momencie wskazania hiperłącza kursorem myszy. Zdarzenie {stala}onmouseover{/stala} jest obsługiwane przez funkcję {stala}getText(){/stala}, której parametrem jest nazwa pliku XML z tekstem piosenki:
function getText(Dane) {
r.open(\'GET\', Dane, true);
r.onreadystatechange = responseAjax;
r.send(null);
}
Ostatnia z funkcji JavaScript odpowiada za wyczyszczenie akapitu, gdy myszka zostanie przesunięta poza obszar hiperłącza. Wystąpienie zdarzenia {stala}onmouseout{/stala} powoduje wywołanie funkcji {stala}clearText(){/stala}:
function clearText() {
document.getElementById(\'tresc\').innerHTML = \'Witaj...\';
document.getElementById(\'tresc\').style.background = \'white\';
}
Zarys skryptu {stala}index.html{/stala}:
...
Witryna pobiera dane o aparatach w sposób asynchroniczny. Na stronie WWW znajdują się wyłącznie nazwy aparatów. Po kliknięciu ikony {stala}+{/stala} skrypt JavaScript pobiera z serwera szczegółowe dane wybranego aparatu. Po odebraniu odpowiedzi we wnętrzu odpowiedniego zielonego prostokąta umieszczane są dane pobrane z serwera. Serwer wysyła dane aparatu w formacie XML.
Jest więc zatem:
- asynchroniczny JavaScript,
- XML,
- modyfikacja strony przy użyciu modelu DOM,
- wymiana fragmentu strony WWW bez przeładowywania całego dokumentu.
Przykład pt. Aparaty fotograficzne w pełni prezentuje możliwości Ajaksa. Strona główna zawiera listę nazw aparatów, każdy z nich jest umieszczony wewnątrz zielonego obszaru div | Z lewej strony nazwy każdego aparatu znajduje się ikona + pozwalająca na wyświetlenie szczegółowych danych. Rysunek obok przedstawia witrynę po rozwinięciu danych aparatu Canon EOS 20D | W tym samym momencie możemy rozwinąć dane dowolnej liczby aparatów. Rysunek obok przedstawia wygląd witryny po rozwinięciu dwóch aparatów |
Rozwijanie i zwijanie jednej kontrolki
Pracę nad witryną Aparaty fotograficzne rozpoczynamy od opracowania pojedynczej zwijanej kontrolki. Kontrolka taka jest zawarta w pojemniku {stala}div#tresc{/stala} i zawiera jedno hiperłącze a, tytuł Pojemnik na treść oraz drugi element {stala}div#minitresc{/stala}:
+
Pojemnik na treść
W obsłudze zdarzenia {stala}onclick{/stala} ikony {stala}+{/stala} należy zmienić wygląd całej kontrolki. W zależności od wartości globalnej zmiennej expanded ukrywamy ({stala}style.display = \’none\'{/stala}) lub pokazujemy ({stala}style.display = \’block\'{/stala}) zawartość pojemnika {stala}div#minitresc{/stala}. Ponadto zmieniamy ikonę oraz wstawiamy do elementu {stala}div #minitresc{/stala} tekst A B C…:
var expanded = false;
function expandCollapse() {
if (expanded) {
expanded = false;
document.getElementById(\'minitresc\').style.display = \'none\';
document.getElementById(\'ikona\').innerHTML = \'+\';
} else {
expanded = true;
document.getElementById(\'minitresc\').style.display = \'block\'
document.getElementById(\'minitresc\').innerHTML = \'A B C...\';
document.getElementById(\'ikona\'.innerHTML = \'-\';
}
}
Rozwijanie i zwijanie wielu kontrolek
Jeśli na stronie WWW ma się znajdować seria podobnych rozwijanych kontrolek, to najlepiej zrezygnować ze stosowania identyfikatorów. Cała kontrolka jest zawarta w elemencie {stala}div{/stala} klasy {stala}tresc{/stala}. Wewnątrz zawiera hiperłącze {stala}a{/stala}, tytuł {stala}span{/stala} oraz dodatkowy element {stala}div{/stala}:
+
Pojemnik na treść
Zwróćmy uwagę na to, że obsługą zdarzenia {stala}onclick{/stala} zajmuje się funkcja {stala}expand-Collapse(){/stala} wywołana z parametrem {stala}this{/stala}. Parametrem {stala}this{/stala} w modelu DOM jest ten węzeł drzewa, który wygenerował zdarzenie (w naszym przypadku: kliknięte hiperłącze). Takie rozwiązanie znacznie uprości treść funkcji {stala}expandCollapse(){/stala}:
function expandCollapse(Id) {
var n = Id.parentNode.childNodes[2];
if (n.style.display == \'block\') {
n.style.display = \'none\'
Id.innerHTML = \'+\';
} else {
n.style.display = \'block\'
n.innerHTML = \'A B C...\';
Id.innerHTML = \'-\';
}
}
To, czy element jest zwinięty, czy rozwinięty stwierdzamy (w warunku instrukcji if) na podstawie wartości właściwości display. Nie wprowadzamy do tego żadnych dodatkowych zmiennych. Zmienna o nazwie {stala}n{/stala} jest drugim elementem {stala}div{/stala} (tj. tym, który poprzednio miał identyfikator minitresc) wewnątrz bieżącej kontrolki. Docieramy do niego następująco:
- {stala}this{/stala} przekazany do funkcji jest klikniętym hiperłączem a,
- parametr funkcji {stala}expandCollapse(){/stala} nazywa się {stala}Id{/stala}, zatem w treści funkcji zamiast {stala}this{/stala} stosowany jest identyfikator {stala}Id{/stala},
- pobieramy rodzica klikniętego hiperłącza ({stala}Id.parentNode{/stala}), czyli element {stala}div.tresc{/stala},
- następnie pobieramy trzecie dziecko elementu {stala}div.tresc{/stala} (pierwsze dziecko: {stala}id.parentNode.childNodes[0{/stala}] – hiperłącze {stala}a{/stala}; drugie dziecko: {stala}id.parentNode.childNodes[ 1]{/stala} – tytuł {stala}span{/stala}; trzecie dziecko: {stala}id.parentNode.childNodes[2]{/stala} – wewnętrzny element {stala}div{/stala}),
- w ten sposób zmienna {stala}n{/stala} odnosi się do obiektu DOM: wewnętrznego elementu div przeznaczonego na treść.
We wnętrzu instrukcji if podobnie jak poprzednio zmieniamy widoczność wewnętrznego elementu div, ustalamy jego treść (A B C…) oraz zamieniamy ikonę plus na minus, a minus na plus.
Kompletny przykład
Przejdźmy do połączenia wszystkich elementów. Wykorzystamy serię rozwijanych kontrolek div oraz Ajax do pobierania szczegółowych danych konkretnego aparatu.
Opisywany przykład składa się z pliku {stala}index.html{/stala} oraz danych w formacie XML, zawartych w folderze {stala}dane-xml/.{/stala}
Wszystkie pliki XML mają identyczną strukturę. Każdy z nich zawiera szczegółowe dane dokładnie jednego aparatu. Na przykład plik {stala}1.xml{/stala} przedstawiony poniżej zawiera szczegółowe dane Canona EOS 20D.
Canon
EOS 20D
DSLR
8.2
1.8
COMOS
...
W treści (tj. pomiędzy znacznikami {html}
{/html} i {html}{/html}) strony umieszczamy serię elementów {stala}div.tresc{/stala}. Jeden element {stala}div.tresc{/stala} dla każdego aparatu:
+
Canon EOS 20D
Są to opisane wcześniej rozwijalne elementy, mające ikony plus (do rozwinięcia) oraz minus (do zwinięcia). Obsługą zdarzenia {stala}onclick{/stala} zajmuje się funkcja {stala}expandCollapse(){/stala}, która tym razem otrzymuje dwa parametry: obiekt {stala}DOM{/stala} o nazwie {stala}this{/stala} (tj. kliknięte hiperłącze) oraz liczbę identyfikującą kliknięty aparat.
W treści funkcji {stala}expandCollapse(){/stala} po pierwsze zmieniamy wygląd elementu {stala}div.tresc{/stala} – zwijamy go lub rozwijamy. Jeśli element jest rozwijany (przypadek else) dodatkowo inicjalizujemy ajaksowy transfer danych. Parametrem metody {stala}open(){/stala} jest skrypt adres URL dokumentu XML ze szczegółową specyfikacją aparatu. Nazwa pliku XML powstaje na podstawie parametru Numer, który identyfikuje kliknięty aparat:
function expandCollapse(Id, Numer) {
element = Id.parentNode.parentNode. childNodes[1];
if (element.style.display == \'block\' {
element.style.display = \'none\';
Id.innerHTML = \'+\';
} else {
element.style.display = \'block\'
Id.innerHTML = \'-\';
r.open(\'GET\', \'dane-xml/\' + Numer + \'.xml\' true);
r.onreadystatechange = processResponse;
r.send(null);
}
}
Ostatnim etapem przygotowania przykładu Aparaty fotograficzne jest opracowanie funkcji {stala}processResponse(){/stala}, która zajmie się umieszczeniem danych odebranych z serwera w rozwiniętym elemencie. Ponieważ wykorzystujemy format XML, należy użyć właściwości {stala}r.responseXML{/stala}. Pobieramy wszystkie dzieci elementu o nazwie aparat:
var x = r.responseXML.getElementsByTagName (\'aparat\')[0].childNodes;
Elementy te przetwarzamy w pętli for, rozpoczynając od elementu o indeksie 2 (elementy 0 oraz 1 to nazwa firmy i nazwa modelu, które są zawarte w tytule wyświetlanego rozwijanego elementu {stala}div{/stala}). Pętla for przygotowuje napis {stala}tmp{/stala}, który jest wstawiony jako treść (tj. {stala}element.innerHTML{/stala}) rozwiniętego elementu: {stala}var{/stala} element;
function processResponse()
{
if (r.readyState == 4) {
if (r.status == 200) {
var x = r.responseXML.getElements ByTagName(\'aparat\'0].childNodes;
var tmp = \'\';
for (i = 2; i < x.length; i++) {
tmp = tmp + \'\' + opis[i] + \': \' + x[i].childNodes[0].nodeValue + \'
\';
}
element.innerHTML = tmp;
}
}
}
Zwróćmy uwagę, że zmienna element jest zmienną globalną. Po raz pierwszy pojawia się ona w funkcji {stala}expandCollapse(){/stala}. Funkcja {stala}expandCollapse(){/stala} umieszcza w zmiennej element rozwinięty div przeznaczony na szczegółowy opis aparatu. W ten sposób funkcja {stala}processResponse(){/stala} nie musi szukać w drzewie DOM elementu, w którym należy wstawić treść. Element ten jest już przygotowany i dostępny w zmiennej element.
Etykiety podpisujące poszczególne parametry aparatu (np. Migawka, Czułość, Autobracketing itd.) są zawarte w tablicy opis zadeklarowanej przed funkcją {stala}processResponse(){/stala}.
Zarys pliku {stala}index.html{/stala}:
+
Canon EOS 20D
+
Canon EOS 30D
...