Przykład pt. „Aparaty fotograficzne” w pełni prezentuje możliwości Ajax-a. Strona główna zawiera listę nazw aparatów, każdy z nich jest umieszczony wewnątrz zielonego obszaru div (rysunek 3).
Z lewej strony nazwy każdego aparatu znajduje się ikona + pozwalająca na wyświetlenie szczegółowych danych. Rysunek 4 przedstawia witrynę po rozwinięciu danych aparatu Canon EOS 20D.
W tym samym momencie możemy rozwinąć dane dowolnej liczby aparatów. Rysunek 5 przedstawia wygląd witryny po rozwinięciu dwóch aparatów.
Wygląd pojedynczego aparatu w formie zwiniętej i rozszerzonej jest przedstawiony na rysunkach 6 oraz 7.
Rysunek 6. Dane pojedynczego aparatu w postaci zwiniętej
Rysunek 7. Dane pojedynczego aparatu w postaci rozwiniętej
Witryna pobiera dane o aparatach w sposób asynchroniczny. Na stronie WWW znajdują się wyłącznie nazwy aparatów. Po kliknięciu ikony +, 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:
Pracę nad witryną Aparaty fotograficzne rozpoczynamy od opracowania pojedynczej zwijanej kontrolki. Kontrolka taka jest zawarta w pojemniku div#tresc i zawiera jedno hiperłącze a, tytuł Pojemnik na treść oraz drugi element div#minitresc:
<div id="tresc"> <a id="ikona" href="#" onclick="expandCollapse();">+</a> Pojemnik na treść <div id="minitresc"></div> </div>
W obsłudze zdarzenia onclick ikony + należy zmienić wygląd całej kontrolki.
W zależności od wartości globalnej zmiennej expanded ukrywamy (style.display = 'none') lub pokazujemy (style.display = 'block') zawartość pojemnika div#minitresc. Ponadto zmieniamy ikonę oraz wstawiamy do elementu div#minitresc 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 = '-';
}
}
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 div klasy tresc. Wewnątrz zawiera hiperłącze a, tytuł span oraz dodatkowy element div:
<div class="tresc"> <a href="#" onclick="expandCollapse(this);">+</a> <span>Pojemnik na treść</span> <div></div> </div>
Uwaga: w rozwiązaniu tym nie możesz umieścić białych znaków pomiędzy elementami HTML. Kod od znacznika <div class="tresc"> do znacznika </div> należy napisać w jednej linijce bez odstępów:
<div class="tresc"><a ...>+</a><span>...</span><div></div></div>
Powodem jest to, że białe znaki będą dodatkowymi dziećmi elementu div.tresc. Odwołanie Id.parentNode.childNodes[2] nie będzie dotyczyło wewnętrznego elementu div.
Zwróć uwagę, że obsługą zdarzenia onclick zajmuje się funkcja expandCollapse() wywołana z parametrem this. Parametrem this 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 expandCollapse():
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 n jest drugim elementem div (tj. tym, który poprzednio miał identyfikator minitresc) wewnątrz bieżącej kontrolki. Docieramy do niego następująco:
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.
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 index.html oraz danych w formacie XML, zawartych w folderze dane-xml/.
Wszystkie pliki XML mają identyczną strukturę. Każdy z nich zawiera szczegółowe dane dokładnie jednego aparatu. Na przykład plik 1.xml przedstawiony na listingu 7 zawiera szczegółowe dane Canona EOS 20D.
<?xml version="1.0" encoding="iso-8859-1"?>
<aparat>
<producent>Canon</producent>
<model>EOS 20D</model>
<typ>DSLR</typ>
<megapixel>8.2</megapixel>
<lcd>1.8</lcd>
<matryca>COMOS</matryca>
...
</aparat>
Listing 7. Fragment pliku XML ze szczegółowymi danymi aparatu Canon EOS 20D
W treści (tj. pomiędzy znacznikami <body> i </body>) W treści strony umieszczamy serię elementów div.tresc. Jeden element div.tresc dla każdego aparatu:
<body>
<div class="tresc">
<h3>
<a href="#" onclick="expandCollapse(this, 1);">+</a>
Canon EOS 20D
</h3>
<div></div>
</div>
<div class="tresc">
<h3>
<a href="#" onclick="expandCollapse(this, 2);">+</a>
Canon EOS 30D
</h3>
<div></div>
</div>
...
</body>
Są to opisane wcześniej rozwijalne elementy, mające ikony plus (do rozwinięcia) oraz minus (do zwinięcia). Obsługą zdarzenia onclick zajmuje się funkcja expandCollapse(), która tym razem otrzymuje dwa parametry: obiekt DOM o nazwie this (tj. kliknięte hiperłącze) oraz liczbę, identyfikującą kliknięty aparat.
W treści funkcji expandCollapse() po pierwsze zmieniamy wygląd elementu div.tresc. Zwijamy go lub rozwijamy. Jeśli element jest rozwijany (przypadek else) dodatkowo inicjalizujemy Ajax-owy transfer danych. Parametrem metody open() 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 processResponse(), 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 r.responseXML. 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 div). Pętla for przygotowuje napis tmp, który jest wstawiony jako treść (tj. element.innerHTML) rozwiniętego elementu:
var element;
function processResponse()
{
if (r.readyState == 4) {
if (r.status == 200) {
var x = r.responseXML.getElementsByTagName('aparat')[0].childNodes;
var tmp = '';
for (i = 2; i < x.length; i++) {
tmp = tmp
+ '<strong>' + opis[i] + ':</strong> '
+ x[i].childNodes[0].nodeValue
+ '<br />';
}
element.innerHTML = tmp;
};
}
}
Zwróć uwagę, że zmienna element jest zmienną globalną. Po raz pierwszy pojawia się ona w funkcji expandCollapse(). Funkcja expandCollapse() umieszcza w zmiennej element rozwinięty div przeznaczony na szczegółowy opis aparatu. W ten sposób funkcja processResponse() 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ą processResponse().
Zarys pliku index.html jest przedstawiony na listingu 8.
<head>
<script type="text/javascript">
function getXMLHttpRequest()
{
...
}
var r;
r = getXMLHttpRequest();
var opis= new Array(20);
opis[0] = 'Producent';
opis[1] = 'Model';
opis[2] = 'Typ';
...
var element;
function processResponse()
{
...
}
function expandCollapse(Id, Numer)
{
...
}
</script>
</head>
<body>
<div class="tresc">
<h3>
<a href="#" onclick="expandCollapse(this, 1);">+</a>
Canon EOS 20D
</h3>
<div></div>
</div>
<div class="tresc">
<h3>
<a href="#" onclick="expandCollapse(this, 2);">+</a>
Canon EOS 30D
</h3>
<div></div>
</div>
...
</body>
Listing 8. Aparaty fotograficzne: zarys pliku index.html