19.8 Ajax
Mit Hilfe der im vorigen Abschnitt vorgestellten DOM-Technik können Sie Inhalte bestehender HTML-Seiten nach Belieben umbauen, ohne diese neu laden zu müssen. Die Ajax-Technik kombiniert dies mit Anfragen an Webserver, die keine ganze Seite neu laden, sondern textbasierte oder XML-basierte Inhalte anfordern. Diese können Sie dann mittels DOM an beliebigen Stellen Ihrer Seite einfügen. Der Vorteil liegt auf der Hand: Anstatt bei jeder Serverinteraktion die komplette Seite neu laden zu müssen (obwohl sich oft nur ein sehr geringer Teil derselben ändert), können Sie einzelne Stellen gezielt mit aktualisierten Daten modifizieren. Das Ziel sind dabei Webanwendungen, die sich so geschmeidig wie Desktopanwendungen präsentieren, anstatt zwischendurch immer wieder leere Seiten und eine Warte-Uhr anzuzeigen.
Der Schöpfer des Begriffs »Ajax«, der Webdesigner Jesse James Garret, behauptet, der Name sei kein Akronym für Asynchronous JavaScript And XML – und doch beschreibt diese Langform recht genau, um was es dabei geht:
- Asynchrone HTTP-Anfragen – Sie finden hinter den Kulissen statt, während die Besucher auf der Seite weiterlesen, ein Formular ausfüllen oder ähnliche Tätigkeiten durchführen. Eine Callback-Funktion liest die Serverantwort, sobald sie vorliegt.
- JavaScript – Das benötigte HTTP-Anfrageobjekt gehört (unter verschiedenen Namen) zur JavaScript-Bibliothek aller aktuellen Browser.
- XML – Die Antwort des Webservers kann (muss aber nicht) verschachteltes XML sein, das Sie ebenso per DOM verarbeiten können wie die Webseite selbst.
19.8.1 Die erste Ajax-Anwendung

Als erste kleine Testanwendung soll jeder Klick auf einen Link ein zufälliges Zitat von einem PHP-Skript anfordern. Dieses Zitat wird an einer bestimmten Stelle der – ansonsten gleichbleibenden – Seite eingetragen.
Ein Ajax-Anfrageobjekt erzeugen
Damit Ihre JavaScript-Anwendungen eigenständige HTTP-Anfragen durchführen können, benötigen Sie zuerst ein Anfrageobjekt. Ist dieses Objekt erst einmal erstellt, sind seine Eigenschaften und Methoden immer dieselben. Zuvor gibt es aber zunächst das Problem, dass die zu instantiierende Klasse je nach Browser drei verschiedene Namen besitzen kann – einen für Firefox, Safari, Chrome und so weiter und zwei andere für unterschiedlich aktuelle Internet-Explorer-Versionen.
Browser-Varianten mit try/catch abfangen
Der folgende Codeblock verwendet try/catch-Blöcke, um die jeweils passende Klasse automatisch zu wählen oder eine Fehlermeldung auszugeben, falls Ajax-Anwendungen im vorliegenden Browser gar nicht möglich sind:
var anfrage = null;
try {
anfrage = new XMLHttpRequest();
} catch (err_ff) {
try {
anfrage = new ActiveXObject("Msxml2.XMLHTTP");
} catch (err_ms1) {
try {
anfrage = new ActiveXObject("Microsoft.XMLHTTP");
} catch (err_all) {
anfrage = null;
}
}
}
if (anfrage == null) {
alert ("Sie verwenden einen nicht Ajax-fähigen Browser.");
}
In den meisten Fällen ist es am praktischsten, diesen Code statisch (außerhalb einer Funktion) in einen JavaScript-Block im Head der Seite zu setzen. Die Alternative wäre eine entsprechende Funktion in einer .js-Datei, die ein fertiges Anfrageobjekt zurückgibt.
Die Ajax-Anfrage vorbereiten und versenden
Um eine Ajax-Anfrage abzuschicken, werden die Methoden open() und send() des Anfrageobjekts verwendet. open() benötigt drei Argumente:
- Die Anfrage-Methode, "POST" oder "GET" – beachten Sie für den Unterschied die Diskussion in Kapitel 17, »Webseitenerstellung mit (X)HTML und CSS«.
- Die Anfrage-URL – in aller Regel die URL eines serverseitigen Skripts. Beachten Sie, dass das entsprechende Skript kein vollständiges HTML-Dokument als Antwort erzeugen soll, sondern lediglich den relevanten Datenanteil. Mögliche Formate sind einfacher Text, diverse XML-Formate oder JSON (siehe»Datenaustauschformate: XML und JSON«, Seite 1042).
- Asynchronität der Anfrage (true oder false). Wenn Sie diesen letzten Parameter auf true setzen, wird die Anfrage asynchron versandt, so dass Sie weiterarbeiten können, während der Browser auf die Antwort wartet. false versendet sie dagegen synchron, und Sie müssen auf die Antwort warten wie auf das Neuladen einer ganzen Seite. Letzteres scheint zwar dem Sinn von Ajax zu widersprechen, ist aber in manchen Fällen nützlich, etwa um ein Formular nach der vollständigen Eingabe komplett zu überprüfen.
Für das Zitate-Beispiel muss als Erstes die URL definiert werden, an die die Anfrage gesendet wird. Das PHP-Skript soll zitat.php heißen und im selben Webserver-Verzeichnis liegen wie die HTML/JavaScript-Datei selbst. Ein weiter oben bereits angesprochenes Problem ist, dass manche Browser die Antwort auf eine GET-Anfrage im Cache speichern, so dass der Inhalt ab dem zweiten Aufruf nicht mehr geändert wird. Die folgende Zeile hängt die aktuelle Zeit (in Sekunden seit EPOCH) als Dummy-Parameter an die eigentliche URL an, so dass formaljuristisch gesehen jedes Mal eine neue URL entsteht:
var url = "zitat.php?dummy=" + new Date().getTime();
Der open()-Aufruf selbst sieht daraufhin so aus:
anfrage.open ("GET", url, true);
Es wird also die Anfrage-Methode GET verwendet, und der Parameter true macht die Anfrage asynchron.
onreadystatechange
Als Nächstes müssen Sie die Eigenschaft onreadystatechange des Anfrageobjekts setzen. Ihr Wert ist der Name einer Callback-Funktion (siehe Kapitel 10, »Konzepte der Programmierung«), die bei jeder Änderung des Bereitschaftszustands (Eigenschaft readyState) Ihres Anfrageobjekts aufgerufen wird. Es gibt insgesamt fünf Bereitschaftszustände, die anzeigen, wie weit die Bearbeitung der Anfrage bereits fortgeschritten ist:
- 0 – Anfrage wird nicht bearbeitet.
- 1 – Verbindung wurde aufgebaut.
- 2 – Anfrage wurde vollständig versandt.
- 3 – Empfang der Antwort beginnt.
- 4 – Antwort liegt vollständig vor.
Bei etwa 99 % aller Ajax-Anwendungen kümmert sich die Callback-Funktion aus nachvollziehbaren Gründen nur um den Zustand 4.
Die (als Nächstes besprochene) Callback-Funktion für das Zitate-Beispiel heißt hole-Zitat(), so dass die betreffende Zeile so aussieht:
anfrage.onreadystatechange = holeZitat;
Beachten Sie, dass hinter dem Funktionsnamen des Callbacks an dieser Stelle keine Parameterklammern stehen dürfen.
Die Ajax-Anfrage senden
Zum Schluss wird die Anfrage versendet. Die Methode send() besitzt ein Argument, das den Body der HTTP-Anfrage repräsentiert. Da GET-Anfragen im Gegensatz zu POST keinen Body haben, ist der Wert im vorliegenden Fall null:
anfrage.send (null);
Alle genannten Codezeilen stehen in einer Funktion namens tauscheZitat(), die per Klick auf den folgenden Hyperlink aufgerufen wird:
<a href="#" onclick="tauscheZitat();">Zitat wechseln</a>
Die gesamte Funktion tauscheZitat() sieht somit wie folgt aus:
function tauscheZitat() {
// URL mit Zeit als Cache-Schutz kombinieren
var url = "zitat.php?dummy=" + new Date().getTime();
// Anfrage eröffnen: Methode GET, URL, asynchron
anfrage.open ("GET", url, true);
// Callback-Funktion für Zustandswechsel festlegen
anfrage.onreadystatechange = holeZitat;
// Anfrage senden (mit leerem Body, da GET)
anfrage.send (null);
}
Die Serverantwort verarbeiten
Wie bereits erwähnt, wird die Callback-Funktion holeZitat() bei jedem Wechsel des Bereitschaftszustands aufgerufen. Daher muss sie zuerst überprüfen, ob die Anfrage fertig bearbeitet wurde (readyState 4). Dies reicht allerdings nicht als Kriterium für eine qualifizierte Antwort – zusätzlich muss der HTTP-Statuscode (Eigenschaft status des Anfrageobjekts) anzeigen, dass sie brauchbar ist. Wie Sie bereits aus Kapitel 13, »Server für Webanwendungen«, wissen, lautet der Statuscode für eine gültige Ressource 200. Das Auslesen der Antwort wird mit anderen Worten durch folgende Prüfung umschlossen:
if (anfrage.readyState == 4) {
if (anfrage.status == 200) {
// Antworttext auslesen und eintragen
// ...
} else {
alert ("Fehlerhafte Server-Antwort: " +
anfrage.status);
}
}
Wie Sie sehen, wird für den Fall, dass der Status nicht 200 ist, eine Fehlermeldung ausgegeben. Bei einem anderen readyState als 4 geschieht dagegen gar nichts, weil alle anderen Bereitschaftszustände irrelevant sind.
responseText – einfache Text-Antwort
Nun fehlen noch die Zeilen zum Auslesen der eigentlichen Serverantwort und zum Ändern des Zitat-Blocks auf der Webseite. Da die Antwort kein XML, sondern reiner Text ist, können Sie sie fix und fertig aus der Eigenschaft responseText des Anfrageobjekts auslesen:
var antwort = anfrage.responseText;
Das Zitat steht im HTML-Dokument in einem <div> namens zitat; anfangs enthält es nur ein geschütztes Leerzeichen:
<div id="zitat" style="color: #FF0000"> </div>
Um das aus dem PHP-Skript gelesene Zitat an dieser Stelle einzutragen, genügt es, den einzigen (und damit unter anderem ersten) Kindknoten dieses <div>-Elements auf den Wert der Variablen-Antwort zu setzen:
document.getElementById("zitat").firstChild.nodeValue =
antwort;
Hier noch einmal die gesamte Funktion holeZitat():
function holeZitat() {
// Nur aktiv werden, wenn Bereitschaftszustand 4
if (anfrage.readyState == 4) {
// Gültige Antwort (Status 200)?
if (anfrage.status == 200) {
// Text der Server-Antwort auslesen
var antwort = anfrage.responseText;
// Zitat in das Dokument einfügen
document.getElementById("zitat").firstChild.
nodeValue = antwort;
} else {
// Ungültige Antwort
alert ("Fehlerhafte Server-Antwort: " +
anfrage.status);
}
}
}
Das PHP-Skript
Selbstverständlich können Sie jede beliebige serverseitige Technologie verwenden, um die Skripte oder Programme zu schreiben, die die Antworten auf die Ajax-Anfragen liefern. In diesem Buch wird PHP verwendet, weil diese Sprache bereits im vorigen Kapitel eingeführt wurde. Das folgende kurze Skript müssten Sie daher auch problemlos verstehen. Die einzige Besonderheit besteht darin, dass seine Ausgabe kein vollständiges HTML-Dokument, sondern nur das reine, jeweils ausgewählte Zufallszitat ist:
<?php
// Array mit allen Zitaten erzeugen
$zitate = array (
"\"To be is to do.\" -- Socrates",
"\"To do is to be.\" -- Sartre",
"\"Do be do be do.\" -- Sinatra"
);
// Ein zufälliges Zitat auswählen
$zitat = $zitate[array_rand($zitate)];
// Das Zitat ausgeben
echo $zitat;
?>
Speichern Sie dieses Skript unter dem Namen zitat.php in einem Site-Verzeichnis Ihres Webservers. Im gleichen Verzeichnis wird auch die nachfolgend vollständig abgedruckte HTML/JavaScript-Datei gespeichert.
Die vollständige Ajax-HTML-Datei
Der Vollständigkeit halber wird hier noch einmal die gesamte HTML-Datei mit dem ausführlich kommentierten Ajax-JavaScript-Code abgedruckt. Speichern Sie diese Datei im gleichen Verzeichnis wie das PHP-Skript. Anschließend können Sie sie über Ihren lokalen Webserver im Browser aufrufen. Sobald Sie den Link Zitat wechseln anklicken, wird ein anderes Zitat nachgeladen (da es nur drei verschiedene gibt, bemerken Sie das allerdings möglicherweise nicht jedes Mal; in der Praxis würde man besser ein Zufallszitat aus einer Datenbank laden).
<html>
<head>
<title>Kleiner Ajax-Test</title>
<script language="JavaScript" type="text/javascript">
<!--
// Ajax-Anfrageobjekt erzeugen
// Zunächst die Referenzvariable deklarieren
var anfrage = null;
try {
// Klasse für Firefox, Opera & Co.
anfrage = new XMLHttpRequest();
} catch (err_ff) {
try {
// Klasse für neuere IEs
anfrage = new ActiveXObject("Msxml2.XMLHTTP");
} catch (err_ms1) {
try {
// Klasse für ältere IEs
anfrage = new ActiveXObject("Microsoft.XMLHTTP");
} catch (err_all) {
// Inkompatibler Browser -- keine Klasse passt
anfrage = null;
}
}
}
if (anfrage == null) {
// Fehlermeldung, falls kein Anfrage-Objekt erzeugt
alert ("Sie verwenden einen nicht Ajax-fähigen Browser.");
}
// tauscheZitat(): Wird bei Klick auf einen Link
// aufgerufen; lädt neues Zitat per Ajax-Anfrage
function tauscheZitat() {
// URL mit Zeit als Cache-Schutz kombinieren
var url = "zitat.php?dummy=" + new Date().getTime();
// Anfrage eröffnen: Methode GET, URL, asynchron
anfrage.open ("GET", url, true);
// Callback-Funktion für Zustandswechsel festlegen
anfrage.onreadystatechange = holeZitat;
// Anfrage senden (mit leerem Body, da GET)
anfrage.send (null);
}
// holeZitat(): Callback-Funktion, die nach
// Ajax-Anfrage die Server-Antwort ausliest und anzeigt
function holeZitat() {
// Nur aktiv werden, wenn Bereitschaftszustand 4
if (anfrage.readyState == 4) {
// Gültige Antwort (Status 200)?
if (anfrage.status == 200) {
// Text der Server-Antwort auslesen
var antwort = anfrage.responseText;
// Zitat in das Dokument einfügen
document.getElementById("zitat").firstChild.
nodeValue = antwort;
} else {
// Ungültige Antwort
alert ("Fehlerhafte Server-Antwort: " +
anfrage.status);
}
}
}
//-->
</script>
</head>
<body>
Hier steht ein dynamisch austauschbares Zufallszitat:
<div id="zitat" style="color: #FF0000"> </div>
<a href="#" onclick="tauscheZitat();">Zitat wechseln</a>
</body>
</html>
19.8.2 Datenaustauschformate: XML und JSON

Das erste Beispiel zeigte bereits den Ablauf einer asynchronen Anfrage und die Zusammenarbeit mit einem serverseitigen Skript, das kein komplettes Dokument zurückliefert, sondern nur Einzelinhalte zum Austauschen. Es handelte sich allerdings nur um einfachen Text – was übrigens trotz des »x« im Namen Ajax recht häufig vorkommt. Falls das Serverskript aber mehrere Daten liefern soll, brauchen Sie irgendein Format, um diese zu organisieren.
Natürlich steht es Ihnen frei, einen String mit beliebigen Trennzeichen zusammenzubasteln und dann per JavaScript »zu Fuß« zu parsen. Üblich ist das aber nicht, und empfehlenswert auch nicht; die meisten Entwickler verwenden eines der folgenden beiden Formate:
- XML – Diese Auszeichnungssprache haben Sie bereits in Kapitel 15, »XML«, kennengelernt. Falls das Format der Daten XML ist, stehen sie in der Eigenschaft responseXML des Ajax-Anfrageobjekts bereit. Ihr Inhalt ist ein DOM-Baum, den Sie nach den bekannten Regeln zerlegen können.
- JSON (JavaScript Object Notation) – Die Daten werden als beliebig tief verschachteltes JavaScript-Array geliefert. Da es sich nicht um XML handelt, werden sie aus der Eigenschaft responseText gelesen und anschließend per eval() von einem String in ein JavaScript-Objekt umgewandelt. Für die meisten Serversprachen gibt es Bibliotheken, die JSON-Daten aus den nativen Datenformaten dieser Sprachen erzeugen. Weiter unten lernen Sie beispielsweise die JSON-Funktionen von PHP kennen.
Ob Sie sich letztlich für XML, für JSON oder doch für ein eigenes Format entscheiden, ist Geschmackssache. Es folgt ein etwas umfangreicheres Praxisbeispiel, das mit beiden Standardformaten gezeigt wird.
19.8.3 Größeres Beispiel: eine interaktive Länderliste

Eine der ersten Ajax-Anwendungen, die einem größeren Publikum bekannt wurden, heißt Google Suggest. Bereits während der Eingabe eines Suchbegriffs werden Vorschläge eingeblendet, sortiert nach der Anzahl der Suchtreffer. Die hier beschriebene Beispielanwendung verwendet eine ähnliche Technik, um eine Liste aller Staaten der Welt nach jedem Tastendruck anhand der bisherigen Eingabe zu filtern. Zusätzlich erscheint jeder Name als Hyperlink. Bei einem Rollover mit der Maus wird an der Mausposition ein Layer eingeblendet, der die dynamisch vom Server nachgeladene Hauptstadt anzeigt.
Die Länderliste liegt in einer MySQL-Datenbanktabelle namens laender, die sich wiederum innerhalb einer Datenbank namens welt befindet und folgendes Schema aufweist:
id | name | hauptstadt
-----+-----------------+-----------------
L1 | Afghanistan | Kabul
L2 | Ägypten | Kairo
... | ... | ...
L194 | Zypern | Nikosia
Die id ist absichtlich keine Zahl, sondern ein jeweils mit »L« beginnender String, denn aus der Liste soll ein assoziatives und kein numerisches Array erstellt werden. Diese Daten werden in einem PHP-Skript aus der Datenbank gelesen und anschließend an das JavaScript geschickt – in der ersten Fassung im XML-Format und danach als JSON-Objekt. An dieser Stelle betreibt die Clientseite der Anwendung ihr eigenes »Caching«: Die gesamte Liste wird nur einmal heruntergeladen und in einem Array gespeichert.
Die Erzeugung des Ajax-Anfrageobjekts wird in eine Datei namens ajax.js ausgelagert und ist identisch mit dem Code aus dem vorigen Beispiel. Das gilt im Grunde für jede Ajax-Anfrage – es sei denn, Sie brauchen einmal mehrere Anfrageobjekte; in diesem Fall können Sie eine Funktion schreiben, die nach dem gezeigten Schema ein solches Objekt erzeugt und zurückgibt, und sie danach beliebig oft aufrufen.
Das PHP-Skript
Das kurze PHP-Skript laender.php, das die Daten im XML-Format liefert, sieht wie folgt aus:
<?php
// Datenbank-Parameter (anpassen)
$host = "localhost";
$user = "dbuser";
$pass = "Ihr Passwort";
$db = "welt";
// Datenbankverbindung (mysqli)
$conn = new mysqli ($host, $user, $pass, $db);
// Dokumentbeginn: XML-PI, öffnendes <laender>-Tag
$laender = "<?xml version=\"1.0\"
encoding=\"iso-8859-1\" ?>";
$laender .= "<laender>";
// SQL-Abfrage
$sql = "SELECT id, name FROM laender
ORDER BY name ASC";
$query = $conn->query ($sql);
// Daten aus der Datenbank lesen
while (list ($id, $land) = $query->fetch_row()) {
// als <land>-Element in den XML-Code schreiben
$eintrag =
"<land><id>$id</id><name>$land</name></land>";
$laender .= $eintrag;
}
// Schließendes </laender>-Tag
$laender .= "</laender>";
// MIME-Type und Dokument ausgeben
header ("Content-type: text/xml");
echo ($laender);
?>
Die XML-Ausgabe
Hier passiert so weit noch nichts Unbekanntes. Natürlich gibt es auch für PHP geeignete Bibliotheken, die aus einem DOM-Baum sauberen XML-Code erzeugen können. Die vorliegende XML-Struktur ist allerdings sehr einfach, so dass es genügt, sie als String aufzubauen. Die Ausgabe ist das folgende XML-Dokument:
<?xml version="1.0" encoding="iso-8859-1" ?>
<laender>
<land><id>L1</id><name>Afghanistan</name></land>
<land><id>L2</id><name>Ägypten</name></land>
...
<land><id>L194</id><name>Zypern</name></land>
Das JavaScript zur Verarbeitung der Länderdaten
Der JavaScript-Code für die eigentliche Anwendung wurde ebenfalls ausgelagert, und zwar in die Datei laender.js. Ihre Methode liesStaaten(), die durch den Event Handler onLoad des HTML-Dokuments aufgerufen wird, sorgt für die Anfrage an laender.php. Hier der Rumpf dieser Methode:
var url = "laender.php";
anfrage.open ("GET", url, false);
anfrage.onreadystatechange = holeListe;
anfrage.send (null);
Eine synchrone Anfrage
Das dritte Argument von open() lautet diesmal false – es handelt sich also um eine synchrone Anfrage, die blockiert, bis die Antwort vorliegt (genauer gesagt bis zum Wechsel von readyState). Dieses Verfahren bietet sich hier an, weil die Anwendung erst nutzbar ist, nachdem die Länderliste geladen wurde.
Für das Einlesen der Länderliste ist die Funktion holeListe() zuständig. Nachdem sie wie üblich sichergestellt hat, dass der readyState 4 und der status 200 ist, liest sie das Ergebnis aus der Eigenschaft responseXML des Anfrageobjekts:
var laenderXML = anfrage.responseXML;
XML als DOM-Baum
Es handelt sich, wie bereits erwähnt, um einen DOM-Baum. Aus diesem werden mit Hilfe bereits bekannter DOM-Methoden die ID-Name-Paare ausgelesen und in ein verschachteltes Array gepackt (die Array-Variable staaten wurde zuvor als global angelegt):
var laenderXML = anfrage.responseXML;
var laender = laenderXML.getElementsByTagName("land");
for (i = 0; i < laender.length; i++) {
var id = laender[i].getElementsByTagName("id")[0].
firstChild.nodeValue;
var land = laender[i].getElementsByTagName("name")[0].
firstChild.nodeValue;
var eintrag = new Array(id, land);
staaten.push (eintrag);
}
Anschließend wird die Methode filterListe() aufgerufen. Sie zeigt alle Elemente der Liste an, die dem aktuellen Filterkriterium entsprechen. Dies ist zu Beginn der leere String, so dass alle Staaten angezeigt werden. Die HTML-Seite enthält ein Textfeld, das nach jedem Tastendruck ebenfalls filterListe() aufruft:
<input type="text" name="eingabe"
onkeyup="filterListe();" />
Den Code von filterListe() sehen Sie weiter unten im Listing. Die wesentlichen Arbeitsschritte sind das Löschen der bisherigen Anzeige durch Aufruf von loeschListe() und die Untersuchung aller Elemente des Arrays staaten in einer Schleife, wobei alle zum aktuellen Filter passenden Ländernamen ausgegeben werden. Dabei werden auch die Event Handler onmouseup und onmousedown erzeugt.
Tooltip durchRollover
Sobald ein Ländername mit der Maus berührt wird, aktiviert dies die Funktion zeigeHauptstadt(). Diese speichert zunächst die Position für den anzuzeigenden Tooltip: die aktuelle Mausposition, um je 10 Pixel nach rechts und nach unten verschoben. Das Auslesen der Mauskoordinaten funktioniert im Internet Explorer anders als in Mozilla-Browsern:[78]
mausX = document.all ? window.event.offsetX + 10 :
e.pageX + 10;
mausY = document.all ? window.event.offsetY + 10 :
e.pageY + 10;
Anschließend wird die ID für die Datenbankabfrage aus der URL des berührten Links gelesen:
var id = this.toString();
id = id.substring (id.indexOf ("#") + 1);
Die ID wird per asynchroner GET-Anfrage an das Skript hauptstadt.php gesendet. Es besteht aus folgenden Zeilen:
<?php
// (Datenbankparameter)
// ...
// Datenbankverbindung
$conn = new mysqli ($host, $root, $pass, $db);
// ID aus der URL ermitteln
$id = $_GET['id'];
// SQL-Abfrage senden
$sql = "SELECT name, hauptstadt FROM laender
WHERE id=\"$id\"";
$query = $conn->query ($sql);
// Land und Hauptstadt lesen
list ($land, $hauptstadt) = $query->fetch_row();
// Datentyp und -inhalt ausgeben
header ("Content-type: text/plain;
charset=iso-8859-1");
echo ("$land: $hauptstadt");
?>
Wie Sie sehen, besteht die Antwort aus einem String in der Form »Land: Hauptstadt«. Sobald die Antwort eintrifft, wird die JavaScript-Callback-Methode holeHauptstadt() aktiv. Sie blendet den Tooltip mit dem neuen Text an der zuvor berechneten Position ein.
Das komplette Skript
Hier der gesamte Code des Skripts laender.js:
// Array für die Staaten vorbereiten
var staaten = new Array();
// Globale Variablen für Mauspositionen
var mausX = null;
var mausY = null;
// Wartezustand
var warten = true;
// loescheListe(): Bisherige Anzeige löschen
function loescheListe() {
liste = document.getElementById ("laenderliste");
while (liste.hasChildNodes()) {
liste.removeChild (liste.firstChild);
}
}
//loescheFilter(): Eingabefeld leeren; alles ausgeben
function loescheFilter() {
document.filter.eingabe.value = "";
document.filter.eingabe.focus();
filterListe();
}
// liesStaaten(): Ajax-Anfrage zu Beginn, um die Liste
// aller Staaten auszulesen
function liesStaaten() {
// URL des PHP-Skripts für die Staatenliste
var url = "laender.php";
// Anfrage eröffnen: Methode GET, URL, synchron(!)
anfrage.open ("GET", url, false);
anfrage.onreadystatechange = holeListe;
anfrage.send (null);
}
// holeListe(): Liste der Staaten abholen und anzeigen
function holeListe() {
// Antwort angekommen?
if (anfrage.readyState == 4) {
// Antwort akzeptabel?
if (anfrage.status == 200) {
warten = false;
// Antwort speichern
var laenderXML = anfrage.responseXML;
// Über alle <land>-Tags im DOM-Baum iterieren
var laender =
laenderXML.getElementsByTagName("land");
for (i = 0; i < laender.length; i++) {
var id = laender[i].getElementsByTagName("id")
[0].firstChild.nodeValue;
var land = laender[i].getElementsByTagName
("name")[0].firstChild.nodeValue;
var eintrag = new Array(id, land);
staaten.push (eintrag);
}
// Anzeige löschen
loescheListe();
// Alle Staaten eintragen (leerer Filter!)
filterListe();
} else {
alert ("Staatenliste nicht verfuegbar!");
}
}
}
// zeigeHauptstadt(): Ajax-Anfrage zum Anzeigen der
// Hauptstadt
function zeigeHauptstadt(e) {
if (warten) {
return;
}
mausX = document.all ? window.event.offsetX + 10 :
e.pageX + 10;
mausY = document.all ? window.event.offsetY + 10 :
e.pageY + 10;
var id = this.toString();
id = id.substring (id.indexOf ("#") + 1);
var url = "hauptstadt.php?id=" + id;
anfrage.open ("GET", url, true);
anfrage.onreadystatechange = holeHauptstadt;
anfrage.send (null);
}
// holeHauptstadt(): Hauptstadt abholen und anzeigen
function holeHauptstadt() {
if (anfrage.readyState == 4 && anfrage.status == 200) {
var hs = anfrage.responseText;
var dieBox = document.getElementById ("hauptstadt");
dieBox.firstChild.nodeValue = hs;
dieBox.style["visibility"] = "visible";
dieBox.style["left"] = mausX + "px";
dieBox.style["top"] = mausY + "px";
}
}
// versteckeHauptstadt(): HS-Ebene verstecken
function versteckeHauptstadt() {
var dieBox = document.getElementById ("hauptstadt");
dieBox.style["visibility"] = "hidden";
}
// filterListe(): Liste anhand der Texteingabe filtern
function filterListe() {
// Bisherigen Text entfernen
loescheListe();
// Suchfilter lesen
var fText = document.filter.eingabe.value;
// Alle Staaten eintragen, auf die der Filter passt
for (i in staaten) {
var eintrag = staaten[i];
var id = eintrag[0];
var land = eintrag[1];
if (land.toLowerCase().indexOf
(fText.toLowerCase()) >= 0) {
var aNode = document.createElement ("a");
var href = document.createAttribute ("href");
href.nodeValue = "#" + id;
aNode.setAttributeNode (href);
aNode.onmouseover = zeigeHauptstadt;
aNode.onmouseout = versteckeHauptstadt;
var tNode = document.createTextNode (land);
var bNode = document.createElement ("br");
aNode.appendChild (tNode);
liste.appendChild (aNode);
liste.appendChild (bNode);
}
}
}
Die HTML-Datei
Aus Gründen der Vollständigkeit sehen Sie hier noch die eigentliche Seite, laenderfilter.html:
<html>
<head>
<title>Ajax-Länderfilter</title>
<meta http-equiv="Content-type" content="text/html; charset=iso-8859-1"
/>
<script language="JavaScript" type="text/javascript" src="ajax.js">
</script>
<script language="JavaScript" type="text/javascript" src="laender.js">
</script>
</head>
<body onload="liesStaaten();">
<h1>Alle Staaten der Erde</h1>
<form name="filter">
Suchfilter:
<input type="text" name="eingabe" onkeyup="filterListe();" />
<input type="button" value="Zurücksetzen"
onclick="loescheFilter();" />
</form>
<div id="laenderliste">
Bitte warten ...
</div>
<span id="hauptstadt" style="position: absolute; top: 0px; left: 0px;
visibility:hidden; background-color: #00FFFF"> </span>
</body>
</html>
Abbildung 19.2 zeigt das Skript im Einsatz.

Abbildung 19.2 Der Ajax-Länderfilter nach Eingabe von »de«
JSON-Version
Die JSON-Version der Länderliste wird zunächst als verschachteltes PHP-Array erzeugt. Anschließend kommt die Funktion json_encode()[79] zum Einsatz, um ein äquivalentes JavaScript-Konstrukt zu erzeugen. Unglücklicherweise kann diese Funktion nur mit UTF?8-Zeichen umgehen, so dass Sie die ISO-Latin-1-Strings aus der Datenbank zunächst umwandeln müssen; dies erledigt die PHP-Funktion utf8_encode(). Hier der vollständige Quellcode von laender.php in der geänderten Fassung:
<?php
// (Datenbankparameter)
// ...
// Datenbankverbindung
$conn = new mysqli($host, $root, $pass, $db);
$laender = array();
// Datenbankabfrage
$sql = "SELECT id, name FROM laender
ORDER BY name ASC";
$query = $conn->query ($sql);
while (list ($id, $land) = $query->fetch_row()) {
// Daten in UTF-8 konvertieren und in Array packen
$eintrag = array(utf8_encode($id),
utf8_encode($land));
array_push($laender, $eintrag);
}
// JSON-Code erzeugen
$ausgabe = json_encode($laender);
// Ausgabe
header ("Content-type: text/plain; charset=utf-8");
echo ($ausgabe);
?>
Das JSON-Format
Die erzeugten Daten haben das folgende Format:
[["L1","Afghanistan"],["L2","Ägypten"],...,
["L194","Zypern"]]
Es handelt sich also bereits um das fertige JavaScript-Array. Dies vereinfacht die Funktion holeListe() enorm; sie kann sich den DOM-Aufwand sparen und braucht das Array nur noch mittels eval() auszupacken:
staaten = eval (anfrage.responseText);
Beachten Sie zuletzt, dass auch bei der zugehörigen Variante von hauptstadt.php die UTF?8-Konvertierung erfolgen muss. Außerdem braucht diese Fassung von laenderfilter.html einen HTTP-Header oder ein Meta-Tag für diesen Zeichensatz. Sie können beide Versionen mit allen zugehörigen Skripten auf der Website zum Buch herunterladen.
Die wichtigsten Ajax-Aufgaben, etwa die Erzeugung des Anfrageobjekts und die Manipulation von DOM-Bäumen, sind immer gleich. Deshalb können Sie sich die Arbeit erleichtern, indem Sie eines der vielen frei verfügbaren Ajax-Frameworks verwenden. Diese automatisieren diese Aufgaben mit unterschiedlichen Schwerpunkten. Bekannte Beispiele sind jQuery (http://jquery.com/), script.aculo.us (http://script.aculo.us/), Prototype (http://prototype.conio.net/) und dojo (http://dojotoolkit.org/). Downloads und Dokumentationen finden Sie auf den angegebenen Websites. Im nachfolgenden Kasten erfahren Sie das wichtigste über jQuery.
Ihr Kommentar
Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.
>> Zum Feedback-Formular



Jetzt bestellen







