JavaScript: Serverkommunikation und dynamische Webanwendungen (Ajax)

Entstehung von Ajax: Neue dynamische Webanwendungen

In der Anfangszeit bestand das World Wide Web aus einer Sammlung von weltweit abrufbaren wissenschaftlichen Daten und Dokumenten. Das heutige Web hingegen bietet nicht bloß statische Informationsangebote, sondern maßgeblich interaktive Dienste. Spätestens unter dem Schlagwort Web 2.0 sind unzählige sogenannte Webanwendungen aus dem Boden gesprossen, die vorher ungeahnte Dienste ins Web brachten. Man denke nur an Kontaktnetzwerke, die selbst keine Inhalte bereitstellen, sondern ihren Nutzern Kommunikation und das Einstellen eigener Inhalte ermöglichen. Das Web als Verbund von mehreren Rechnern, nicht mehr der einzelne Rechner ist die Plattform für Software dieser Art.

Viele dieser neuen Webanwendungen setzen stark auf JavaScript, um die Funktionalität klassischer Desktop-Anwendungen bereitzustellen. Heraus kommen zum Beispiel E-Mail-Programme, News-Leseprogramme, Textverarbeitung, Tabellenkalkulation, Präsentationen, Chat-Programme, Foto-Verwaltung und Bildbearbeitung, Terminkalender und Adressbücher, Karten-Anwendungen und vieles mehr. Dadurch, dass die Anwendung nun im Netz beheimatet ist, ergeben sich besondere Mehrwerte: Man spricht von »sozialer« und »kollaborativer« Software, deren Fokus auf Kommunikation, gemeinsame Nutzung von Daten und Zusammenarbeit liegt. Mittlerweile genießen diese Anwendungen eine ungeheure Popularität und vereinfachen die Arbeit am Computer und im Netz.

JavaScript spielt dabei eine zentrale und neue Rolle. Es handelt sich nicht um klassische HTML-Dokumente, denen mit JavaScript ein wenig Interaktivität hinzugefügt wird. Stattdessen funktionieren viele dieser Webanwendungen nicht ohne JavaScript. Durch komplexes Event-Handling und viele Tricks bringt JavaScript einfache HTML-Elemente dazu, sich wie Bedienelemente von Desktop-Programmen zu verhalten - z.B. wie Schaltflächen, Menüs oder Dialogfenster.

Was ist anders an Ajax?

Gemeinsam ist den vielen der besagten Webanwendungen eine Schlüsseltechnik namens Ajax. Das bedeutet: JavaScript tauscht im Hintergrund Daten mit dem Webserver aus. Das funktioniert über selbst erzeugte HTTP-Anfragen, deren Server-Antwort dem Script zur Verfügung steht.

Was ist daran nun neu? Dazu muss man zuerst verstehen, wie Interaktion im Web ohne Ajax funktioniert: Herkömmliche Websites nutzen Links und Formulare, um mit dem Webserver zu interagieren. Der Anwender aktiviert einen Link oder sendet ein Formular ab, woraufhin der Browser eine entsprechende HTTP-Anfrage an den Webserver sendet. Der Webserver antwortet, indem er üblicherweise ein HTML-Dokument zurückliefert, das der Browser verarbeitet und anstelle des alten anzeigt. Ohne Ajax muss also immer ein neues, vollständiges HTML-Dokument vom Server geladen werden. Diese HTML-Dokumente werden oft in Webanwendungen oftmals von serverseitigen Programmen generiert.

Ajax durchbricht dieses Prinzip und kann damit die Bedienung von Webseiten und den Aufbau von Webanwendungen grundlegend ändern. Es werden nicht immer neue HTML-Dokumente heruntergeladen und ausgewechselt, sondern nur kleine Datenportionen mit dem Webserver ausgetauscht. Gerade benötigte Daten werden nachgeladen und ausgewählte Änderungen dem Server mitgeteilt.

Im Extremfall kommt eine sogenannte Single Page Application heraus, bei der es nur ein ursprüngliches HTML-Dokument gibt und der restliche Datenaustausch mit dem Webserver per JavaScript im Hintergrund abläuft. Über die DOM-Schnittstelle wird das Dokument nach Belieben umgestaltet. Es reagiert auf Benutzereingaben, übersendet diese gegebenenfalls an den Server, lädt im Hintergrund Inhalte vom Server nach und montiert diese ins bestehende Dokument ein.

Der Begriff »Ajax« und seine Schwächen

Der Begriff Ajax wurde ursprünglich im Jahr 2005 von dem richtungsweisenden Artikel A New Approach to Web Applications von Jesse James Garrett geprägt. Ajax steht darin als Abkürzung für Asynchronous JavaScript and XML (auf Deutsch: asynchrones JavaScript und XML).

Diese Abkürzung stift leider mehr Verwirrung, als sie zum Verständnis beiträgt. Weder sind Ajax-Anwendungen asynchron in dem Sinne, dass die Kommunikation mit dem Server völlig losgelöst von Benutzereingaben stattfindet. Noch ist XML zwangsläufig das Übertragungsformat für Daten zwischen Client und Server. Garretts Konzept taugt wenig zum Verständnis der gegenwärtigen Praxis, die unter dem Schlagwort Ajax zusammengefasst wird.

In den meisten Fällen bezeichnet »Ajax« lediglich den JavaScript-gestützten Datenaustausch mit dem Webserver. XML in dabei nur ein mögliches, aber nicht das zentrale Übertragungsformat. Und asynchron bedeutet lediglich, dass die JavaScript-Ausführung beim Warten auf die Server-Antwort nicht den Browser blockiert, sondern dass JavaScript-Ereignisse gefeuert werden, wenn die Server-Antwort eingetroffen ist.

Vor- und Nachteile von Ajax

Klassisches Modell mit eigenständigen, adressierbaren Dokumenten

Das herkömmliche Modell funktioniert nach dem Grundsatz »Stop and Go«: Der Anwender klickt Anwender auf einen Link oder den Absende-Button eines Formulars und muss erst einmal warten. Der Browser übermittelt derweil eine Anfrage an den Webserver. Dieser speichert gegebenenfalls Änderungen ab und generiert ein neues, vollständiges Dokument. Erst wenn dieses zum Client-Rechner übertragen wurde und der Browser es vollständig dargestellt hat, kann der Anwender in der Webanwendung weiterarbeiten.

Der Browser zeigt also beim klassischen Modell eine Reihe von eigenständigen HTML-Dokumenten (Ressourcen) an, die alle eine eindeutige, gleichbleibende Adresse (URL) besitzen. Dafür stellt der Browser Navigationsmechanismen wie den Verlauf (auch History genannt) zur Verfügung. Der bestechende Vorteil dieses Modells: Diese Dokumente sind unabhängig von JavaScript lesbar, verlinkbar, durch Suchmaschinen indizierbar, problemlos abspeicherbar und so weiter.

Besonderheiten bei Ajax

Mit Ajax hingegen werden die Server-Anfragen im Hintergrund gestartet, ohne dass das Dokument ausgewechselt wird. Damit fällt das Warten auf die Server-Antwort entweder ganz weg, weil nicht auf sie gewartet werden muss, oder der Server muss nur eine kleine Datenportion zurückschicken. Der Vorteil von Ajax-Webanwendungen ist daher, dass sie schneller auf Benutzereingaben reagieren und dem vertrauten Verhalten von Desktop-Anwendungen näherkommen.

Ajax bricht absichtlich mit grundlegenden Funktionsweisen und Regeln des Webs. Daraus zieht es seine Vorteile, aber auch schwerwiegende Nachteile: Es gibt keine vollständigen, adressierbaren Dokumente mehr, die in Webanwendungen einen bestimmten Punkt und Status markieren. Wenn der Anwender zu dem Inhalt kommen möchte, den er zuvor gesehen hat, betätigt er aus Gewohnheit die Zurück-Funktion. Das funktioniert bei der Verwendung von Ajax nicht mehr wie gewohnt: Denn im Browser-Verlauf wird die Änderung der Inhalte via JavaScript nicht registriert, denn das Dokument ist nicht ausgewechselt worden und die Adresse hat sich nicht geändert. Eine Navigation, wie sie der Anwender von statischen Webseiten gewohnt ist, ist auf Seiten mit solcher JavaScript-Interaktivität nicht ohne weiteres möglich.

Zugänglichkeit von Ajax-Anwendungen

Ajax-Anwendungen verlagern einen großen Teil der Datenverarbeitung vom Server-Rechner auf den Client-Rechner, genauer gesagt in den Browser. Damit steigen die Ansprüche, die an die Zugangssoftware gestellt werden - auch wenn mittlerweile alle neueren JavaScript-fähigen Browser über leistungsfähige Ajax-Umsetzungen verfügen.

Es ist eine besondere Herausforderung, eine Site mit Ajax zugänglich für Nutzer ohne JavaScript, mit alternativen oder assistiven Zugangstechniken wie Screenreadern oder Mobilbrowsern zu gestalten. Funktionen wie der Verlauf, den bisher der Browser automatisch zur Verfügung stellte, müssen in Ajax-Anwendungen nachgebaut werden, z.B. indem jeder Status eine Adresse bekommt, damit die Zurück-Navigation funktioniert und der Status verlinkt werden kann. Es ist also mit einigem Aufwand verbunden, eine Ajax-Anwendung so komfortabel und robust zu bekommen, wie es klassische Lösungen von Haus aus sind.

Typische abwärtskompatible Anwendungsfälle von Ajax

Neben vollständigen Webanwendungen, die das Verhalten einer Desktop-Anwendung komplett in JavaScript zu emulieren versuchen, gibt es viele kleine Fälle, in denen Hintergrund-Serverkommunikation auf klassischen Webseiten sinnvoll ist und die Bedienung vereinfacht. In diesen Fällen kann Ajax zumeist als Zusatz verwendet werden. Das heißt: Falls JavaScript verfügbar ist, genießt der Anwender einen gewissen Extra-Komfort in der Bedienung. Falls JavaScript nicht aktiv oder Ajax nicht verfügbar ist, dann kann die Website trotzdem ohne funktionale Einschränkungen benutzt werden. Dieser abwärtskompatible Einsatz entspricht dem Konzept des Unobtrusive JavaScript.

Laden von kleinen Inhaltsfragmenten

Oftmals werden Inhalte in kurzer, übersichtlicher Listenform dargestellt. Um den vollen Eintrag zu sehen, muss ohne Ajax ein neues Dokument geladen werden. Mit Ajax können Listenansicht und Vorschau- bzw. Vollansicht kombiniert werden, ohne dass alle Detail-Informationen von Anfang an versteckt im Dokument liegen. Auf Benutzerwunsch (z.B. beim Klicken oder beim Überfahren mit der Maus) können die Informationen zu einem Eintrag via Ajax nachgeladen werden und erscheinen dann direkt beim entsprechenden Listeneintrag.

Automatische Vervollständigung (Autocomplete und Suche bei der Eingabe)

Ohne Ajax sind Suchmasken mitunter langsam und zäh bedienbar: Man gibt einen Suchbegriff ein, sendet das Formular ab und wartet auf die Trefferliste. In vielen Fällen muss man die Suche verfeinern oder den Suchbegriff korrigieren, z.B. weil man ihn offensichtlich nicht korrekt geschrieben hat. Schneller zum Ziel kommt man, wenn schon beim Tippen die Eingabe im Hintergrund an den Server gesendet wird und unter dem Eingabefeld blitzschnell Suchvorschläge angezeigt werden, die zu der bisherigen Eingabe passen.

Stellen Sie sich etwa eine Fahrplanauskunft vor: Wenn Sie »Paris« in das Zielfeld eingeben, werden sofort alle Pariser Bahnhöfe aufgelistet, sodass sie einen davon wählen können. Oder Sie geben in einem Produktkatalog einen Herstellernamen, eine Produktkennziffer oder ein Merkmal ein, so kann Ajax mithilfe einer intelligenten serverseitigen Suchfunktion die passenden Produkte sofort anzeigen, noch während sie tippen.

Server-Aktionen ohne Antwort-Daten

Nicht jede Benutzeraktion auf einer Site startet das Abrufen von neuen Informationen vom Server. Es gibt viele Aktionen, die dem Server bloß eine Statusänderung mitteilen, ohne dass dazu das aktuelle Dokument ausgetauscht und ein neues aufgebaut werden muss. All diese sind Kandidaten für sinnvollen Ajax-Gebrauch:

Beim erfolgreichen Übermitteln der Aktion an den Server gibt dieser üblicherweise nur eine Bestätigung zurück, das dem Benutzer nicht einmal präsentiert werden muss. Daher kann man dem Benutzer standardmäßig eine Erfolgsmeldung zeigen, ohne dass auf die Server-Antwort gewartet werden muss. Nur wenn diese negativ ausfällt, wird eine Fehlermeldung angezeigt. Dadurch wirkt ein Ajax-Interface besonders schnell bedienbar und reagiert ohne Verzögerung auf Benutzereingaben – vorausgesetzt, dass die HTTP-Anfrage korrekt übermittelt wurde und das serverseitige Programm sie verarbeiten konnte.

Blättern und Seitennavigation

Beim Navigieren durch Listen z.B. mit Suchresultaten, Artikeln oder Produkten gibt es üblicherweise eine Seitennavigation. Seite 1 zeigt etwa die Resultate 1 bis 10, Seite 2 zeigt 11 bis 20 und so weiter. Ajax kann das Durchstöbern dieser Listen vereinfachen, indem beim Blättern nicht notwendig ein neues Dokument vom Server geladen werden muss. Stattdessen kann Ajax die Einträge der folgenden Seite schrittweise hinzuladen und z.B. ans Ende der bestehenden Liste einfügen. Das kann sogar soweit gehen, dass die folgenden Einträge automatisch nachgeladen werden, sobald der Anwender an das Ende der gerade angezeigten Einträge scrollt.

Eingebettete Formulare

Vom Benutzer veränderbare Daten (z.B. ein Kundenprofil oder Einstellungen) werden üblicherweise in zwei Ansichten angezeigt: Die Nur-Lesen-Ansicht einerseits und die Editieren-Ansicht andererseits. Beispielsweise gibt es eine tabellarische Auflistung sowie ein zusätzliches Formular, in dem dieselben Daten verändert werden können.

Mit Ajax können beide Ansichten zu einer zusammengefasst werden (sogenannte Edit-in-place-Formulare bzw. Inline Edit). Will der Benutzer ein Datenfeld editieren, so kann JavaScript die Lese-Ansicht auf Knpofdruck dynamisch in eine Formular-Ansicht wechseln. Hat der Benutzer das Editieren beendet, so werden die Änderungen per Ajax an den Server gesendet. Verlässt der Benutzer die Seite, so bleiben die Änderungen gespeichert und es besteht keine Gefahr, dass der Benutzer vergisst, das Formular abzusenden.

Regelmäßiges Aktualisieren vom Server (Liveticker, E-Mail, Chats)

Anwendungen wie Liveticker oder webbasierte E-Mail-Leseprogramme basieren darauf, dass eine Webseite häufig Aktualisierungen erfährt. Anstatt immer das gesamte Dokument neu zu laden, um eventuelle neue Inhalte anzuzeigen, kann Ajax regelmäßig im Hintergrund beim Server nachfragen, ob seit der letzten Aktualisierung neue Inhalte hinzugekommen sind. Falls ja, sendet der Server diese neuen oder geänderten Einträge in der Antwort gleich mit und JavaScript stellt sie im aktuellen Dokument dar. Ohne dass der Benutzer etwas tun muss, aktualisiert sich die Webseite von selbst und zeigt neue Nachrichten schnellstmöglich an.

Mit Ajax ist bisher keine echte Echtzeit-Aktualisierung möglich, wie sie für Web-basierte Chats und Instant Messaging benötigt wird. Das liegt hauptsächlich daran, dass das Protokoll HTTP auf einem Anfrage-Antwort-Schema anstatt auf einer dauerhaften Verbindung zwischen Client und Server basiert. Man kann zwar alle paar Sekunden einen sogenannten Server-Poll einleiten, d.h. beim Webserver nachfragen, ob neue Chat-Beiträge bzw. Direktnachrichten vorhanden sind. Allerdings lässt sich damit nicht die Geschwindigkeit und Zuverlässigkeit erreichen, wie man sie von echten Chat-Programmen gewohnt ist.

Unter dem Schlagwort Comet werden allerdings Techniken wie Web Sockets und Server-sent Events entwickelt, die diese Beschränkungen von HTTP zu umgehen versuchen. Auch wenn es bereits beeindruckende Praxisanwendungen gibt, so kommen diese nicht ohne aufwändige Tricks aus und haben mit grundlegenden Problemen zu kämpfen.

XMLHttpRequest – die JavaScript-Technik hinter Ajax

Nachdem wir einige Anwendungsfälle betrachtet haben, soll es nun zur Sache gehen: Wie wird Ajax in JavaScript konkret angewendet?

Hintergrund-Serverkommunikation wird in erster Linie mit einer JavaScript-Technik umgesetzt: dem XMLHttpRequest-Objekt. Dies ist ursprünglich eine proprietäre Erfindung von Microsoft für den Internet Explorer. Die Erzeugung eines XMLHttpRequest-Objekt war zunächst an ActiveX gekoppelt, eine weitere Microsoft-Technik. Mithilfe dieses Objekts werden HTTP-Anfragen gestartet und die Server-Antwort ausgelesen.

Andere Browserhersteller erkannten die Möglichkeiten von XMLHttpRequest und übernahmen diese Technik - allerdings ohne ActiveX. Mittlerweile kennen alle großen JavaScript-fähigen Browser das Objekt window.XMLHttpRequest. Ab Version 7 des Internet Explorers ist dieses globale Objekt ebenfalls verfügbar, bei älteren Versionen muss der Umweg über ActiveX genommen werden.

Asynchronität und Event-Basierung bei XMLHttpRequest

Der Clou an XMLHttpRequest ist, dass es eine Server-Anfrage standardmäßig asynchron, d.h. im Hintergrund absendet. Die Server-Antwort wird dann durch Ereignis-Behandlung verarbeitet. Das bedeutet, dass ein Script die Anfrage auslöst und eine angegebene Event-Handler-Funktion aufgerufen wird, sobald sich der Status der Anfrage ändert und schließlich die Antwort eintrifft.

Diese Ereignis-basierte Verarbeitung hat folgenden Sinn: Der Browser friert an der Stelle, wo die Anfrage abgesendet wird, nicht ein und stoppt die Ausführung von JavaScript, bis die Antwort eingetroffen ist. Sondern der Browser kommt zur Ruhe, kann andere Scriptteile ausführen und sogar weitere Server-Anfragen starten. Erst dadurch ist die schnelle und unterbrechungsfreie Reaktion auf Benutzereingaben möglich, die für Ajax-Anwendungen typisch ist.

Wenn Sie noch nicht mit Event-Handling in JavaScript vertraut sind, dann wird Sie dieses Modell erst einmal verwirren. Denn die JavaScript-Funktion, die das XMLHttpRequest erzeugt und die HTTP-Anfrage absendet, kann nicht gleichzeitig die Server-Antwort verarbeiten. Diese Aufgabe muss eine weitere Funktion übernehmen. Wie das konkret aussieht, werden wir später sehen.

In den meisten Fällen sollten Sie asynchrones XMLHttpRequest wählen. Es soll allerdings nicht verschwiegen werden, dass auch synchrones XMLHttpRequest möglich ist. Dies arbeitet nicht Event-basiert, sondern hält die JavaScript-Ausführung vom Zeitpunkt des Absendens der Anfrage bis zum Zeitpunkt des vollständigen Empfangs der Antwort an. Das bedeutet, dass die JavaScript-Anweisung, die auf xhr.send() folgt (siehe unten) direkt Zugriff auf die Server-Antwort hat.

Server-Anfrage mit XMLHttpRequest absenden

Die Absenden einer Anfrage mittels XMLHttpRequest umfasst vier grundlegende Schritte:

Das Erzeugen eines XMLHttpRequest-Objekts

var xhr = new XMLHttpRequest();

Eine neue Instanz wird erzeugt, indem der Konstruktor XMLHttpRequest mit dem Schlüsselwort new aufgerufen wird. Das zurückgelieferte Anfrage-Objekt wird hier in einer Variable namens xhr gespeichert.

Für ältere Internet Explorer ist wie gesagt eine andere Schreibweise nötig, welche ein ActiveX-Objekt erstellt. …

Um browserübergreifend zu arbeiten, benötigen wir eine Fähigkeitenweiche mit einer Objekt-Erkennung. Wenn window.XMLHttpRequest zur Verfügung steht, wird diese Objekt benutzt, andernfalls wird versucht, ein ActiveX-Objekt zu erzeugen. …

Registrieren einer Handler-Funktion für das readystate-Ereignis (bei asynchronen Anfragen)

xhr.onreadystatechange = xhrReadyStateHandler;

Sie müssen eine Funktion angeben, die immer dann aufgerufen wird, wenn sich das Status der Server-Anfrage ändert. Das bewerkstelligt die Zuweisung einer Funktion an die Eigenschaft onreadystatechange. Dieses Schema gleich dem traditionellen Event-Handling. Der Aufbau der readyState-Handler-Funktion wird weiter unten beschrieben.

Diese Anweisung ist nur bei asynchronen Anfragen nötig.

Festlegen der Anfrage-Methode, der Anfrage-Adresse sowie des Arbeitsmodus

xhr.open("GET", "beispiel.html", true);

Mit dem Aufruf der open-Methode des Anfrage-Objekts geben Sie drei zentrale Daten an.

  1. Im ersten Parameter die HTTP-Anfragemethode als String. Üblich sind "GET", "POST" und "HEAD".
  2. Im zweiten Parameter die Adresse (URL) als String, an die die Anfrage gesendet werden soll. Im Beispiel wird davon ausgegangen, dass unter demselben Pfad wie das HTML-Dokument, das die Server-Anfrage startet, eine Datei namens beispiel.html existiert.
  3. Der dritte Parameter legt schließlich fest, ob die Anfrage synchron oder asynchron abgesendet werden soll. Übergeben Sie true für eine asynchrone Abfrage oder false für eine synchrone Anfrage.

Im obigen Beispielcode wird eine asynchrone GET-Anfrage an die Adresse beispiel.html abgesendet.

Anfrage starten und gegebenenfalls POST-Daten angeben

xhr.send(null);

Um die Anfrage zu starten, rufen Sie die send-Methode auf. Im Fall einer GET-Anfrage brauchen Sie keine Parameter angeben - oder nur null wie im Beispiel. Im Falle einer POST-Anfrage übergeben Sie den sogenannten Anfragekörper (englisch POST body). Dieser String wird dann an den Server übermittelt. Die Übertragung von Daten werden wir weiter unten genauer besprechen.

Server-Antwort beim XMLHttpRequest verarbeiten

Vor dem Absenden eines asynchronen XMLHttpRequest haben wir eine Handler-Funktion namens xhrReadyStateHandler registriert. Diese wird beispielhaft wie folgt notiert:

function xhrReadyStateHandler() {
   …
}

Anfrage-Status: readyState

Die Funktion wird immer dann ausgeführt, wenn sich der Status der Anfrage ändert (der sogenannte ready state, auf deutsch in etwa Fortschrittsstatus). Jede Anfrage durchläuft eine Reihe von fünf Phasen, die jeweils durch eine Nummer von 0 bis 4 identifiziert werden.

Der gegenwärtigen Status lässt sich über die Eigenschaft readyState des XMLHttpRequest-Objekts abfragen. Diese Eigenschaft enthält eine Nummer gemäß der folgenden Tabelle.

Status-NummerAnfrage-Zustand
0XMLHttp-Request wurde erzeugt, aber die open-Methode noch nicht aufgerufen
1open wurde aufgerufen, aber noch nicht send
2send wurde aufgerufen und von der Server-Antwort ist bereits der Statuscode und die Antwort-Kopfzeilen (Header) eingetroffen
3Die Antwortdaten werden übertragen und sind schon teilweise verfügbar
4Die Antwortdaten wurden komplett übertragen

In den meisten Fällen ist nur der Status 4 interessant, denn dann können Sie auf die vollständige Server-Antwort zugreifen. Da der readyState-Handler für jeden Status aufgerufen wird, fragen wir darin ab, ob die readyState-Eigenschaft den Wert 4 erreicht hat. Wenn das nicht der Fall ist, brechen wir die Funktion vorzeitig mit return ab. Der restliche JavaScript-Code der Funktion wird dann nur ausgeführt, wenn der Zugriff auf die Server-Antwort möglich ist.

function xhrReadyStateHandler() {
   if (xhr.readyState != 4) {
      return;
   }
   // Server-Antwort ist eingetroffen!
}

HTTP-Statuscode: responseCode

Alleine das Eintreffen der Server-Antwort bedeutet nun nicht, dass der Server die Anfrage fehlerfrei verarbeiten konnte. Ausschlaggebend dafür ist der HTTP-Statuscode der Server-Antwort. Dies ist eine vierstellige Zahl. Im Erfolgsfalle lautet der Code 200, im Fehlerfalle z.B. 404 für »Nicht gefunden« oder 500 für »Server-interner Fehler«. Nur im Erfolgsfalle können Sie damit rechnen, dass der Server die gegebenenfalls übersandten Daten entgegen genommen hat und die Server-Antwort die gewünschten Daten enthält.

Der HTTP-Statuscode der Antwort lässt sich über die Eigenschaft responseCode des jeweiligen XMLHttpRequest-Objekts auslesen. Der readyState-Handler wird daher um eine if-Anweisung ergänzt, die den responseCode mit 200 vergleicht. 200 bedeutet wie gesagt »Alles in Ordnung«.

function xhrReadyStateHandler() {
   if (xhr.readyState != 4) {
      return;
   }
   // Server-Antwort ist eingetroffen!
   if (xhr.responseCode === 200) {
      // Server-Antwort in Ordnung
   }
}

Im Fehlerfall passiert beim obigen Beispiel nichts – das ist natürlich nicht optimal. Denn selbst wenn der Server nicht mit den gewünschten Daten antwortet, so antwortet er zumeist mit einer Fehlermeldung. Das folgende Beispiel enthält in einem else-Zweig eine sehr einfache Fehlerbehandlung:

function xhrReadyStateHandler() {
   if (xhr.readyState != 4) {
      return;
   }
   if (xhr.responseCode === 200) {
      // Server-Antwort in Ordnung
   } else {
      alert("Es ist ein Fehler beim Laden aufgetreten:\n" + xhr.responseCode);
   }
}

Diese kann Ihnen als Seitenbetreiber helfen, den Fehler zu beheben – für Ihre Seitenbesucher ist sie allerdings oftmals wenig hilfreich.

Zugriff auf die Antwortdaten: responseText und responseXML

Nachdem der Anfrage-Status sowie der HTTP-Antwortcode abgefragt wurde, kann endlich auf die tatsächlichen Antwortdaten zugegriffen werden. Dazu stehen zwei Eigenschaften des XMLHttpRequest-Objekts zur Verfügung: responseText und responseXML.

responseText

responseText enthält die Server-Antwort als String. Das Auslesen dieser Eigenschaft ist der Standardweg und die folgenden Beispiele werden diese Eigenschaft verwenden.

responseXML

Wenn es sich bei der Server-Antwort um ein XML-Dokument handelt, erlaubt die Eigenschaft responseXML Zugriff auf das DOM des XML-Dokumentes. Vorausgesetzt ist, dass der Server das Dokument mit einem entsprechenden Inhaltstyp (MIME-Typ) sendet. Dieser lautet ist üblicherweise application/xml. Nur dann verarbeitet der Browser die Server-Antwort automatisch mit seinem XML-Parser und stellt den DOM-Zugriff bereit.

responseXML liefert den Document-Knoten des DOM-Knotenbaums. Von dort aus stehen Ihnen alle Möglichkeiten des W3C-Core-DOM zur Verfügung. Auf das Wurzelelement können Sie beispielsweise über xhr.responseXML.documentElement zugreifen, eine Liste aller Elemente eines Typs bekommen Sie mit den Methoden xhr.responseXMLgetElementsByTagName() bzw. xhr.responseXMLgetElementsByTagNameNS() abfragen.

Server-Antwort aus responseText ins Dokument schreiben

Eine häufige Aufgabe ist es, die Server-Antwort ins aktuelle Dokument einzufügen. Das bekannte Beispiel erweitern wir um eine Zuweisung, die die Antwortdaten in ein Element hineinlädt.

function xhrReadyStateHandler() {
   if (xhr.readyState != 4) {
      return;
   }
   if (xhr.responseCode === 200) {
      document.getElementById("zielelement").innerHTML = xhr.responseText;
   }
}

Mittels getElementById sprechen wir ein vorhandenes Element im Dokument an. Der String mit den Antwortdaten wird dessen Eigenschaft innerHTML zugewiesen. Die Antwortdaten werden damit als HTML-Codeschnipsel interpretiert und werden als neuer Inhalt des angesprochenen Elements eingebunden.

Übrigens ist es auch möglich, responseText zu nutzen, wenn der Server nicht mit dem HTTP-Statuscode 200 geantwortet hat – in diesem Fall können Sie responseText auslesen, um eine eventuelle Fehlermeldung des Servers auszugeben.

Vollständiges XMLHttpRequest-Beispiel

<!DOCTYPE html>
<html><head>
<title>XMLHttpRequest-Beispiel</title>
<script>
function sendXhr() {
   var xhr;
   if (window.XMLHttpRequest) {
      xhr = new XMLHttpRequest();
   } else if (window.ActiveXObject) {
      xhr = new ActiveXObject('MSXML2.XMLHTTP');
   } else {
      return false;
   }
   xhr.onreadystatechange = xhrReadyStateHandler;
   xhr.open("GET", "beispiel.html", true);
   xhr.send(null);
}
function xhrReadyStateHandler() {
   if (xhr.readyState != 4) {
      return;
   }
   if (xhr.responseCode === 200) {
      document.getElementById("zielelement").innerHTML = xhr.responseText;
   }
}
</script>
</head><body>
<p><a href="javascript:sendXhr()">XMLHttpRequest starten</a></p>
<p id="zielelement">Hier erscheint die Server-Antwort.</p>
</body></html>

Datenübertragung mit XMLHttpRequest

Die Vielseitigkeit von XMLHttpRequest rührt daher, das Sie nicht nur Daten vom Server nachladen können, sondern auch Daten aus JavaScript heraus an den Server übermitteln können. Beispielweise lässt sich mit JavaScript der Wert eines Formularelements im Dokument auslesen und per XMLHttpRequest an den Server übertragen. Dieser interpretiert den Wert beispielsweise als Suchbegriff und liefert entsprechende Suchergebnisse zurück.

Wie die Datenübertragung genau abläuft, hängt von der Anfragemethode ab. Wir werden die zwei wichtigsten betrachten: GET und POST.

GET und der Query String

Bei GET werden die zu übertragenden Daten in die Adresse (URL) einmontiert, und zwar in den Teil der URL, der Query String (Abfrage-Zeichenkette) genannt wird. Dieser ist von der eigentlichen URL mit einem Fragezeichen (?) getrennt. Eine relative Adresse mit Query String sieht von Aufbau her so aus:

beispiel.php?querystring

Üblicherweise setzt sich der Query String aus Name-Wert-Paaren zusammen. Name und Wert werden durch ein Gleichheitszeichen (=) getrennt, die Paare durch ein kaufmännisches Und-Zeichen (&). Schematisch ist der Query String folglich so aufgebaut: name1=wert1&name2=wert2&name3=wert3 usw. (Je nach verwendeter Server-Software ist auch ein anderer Aufbau des Query Strings denkbar. Dies ist jedoch der am weitesten verbreitete.)

Wenn wir beispielsweise die drei Informationen Vorname: Alexandra, Nachname: Schmidt und Wohnort: Hamburg übertragen wollen, so könnte der Query String lauten:

vorname=Alexandra&nachname=Schmidt&wohnort=Hamburg

Um diese Daten per GET zu übertragen, werden sie in die relative Adresse einmontiert:

beispiel.php?vorname=Alexandra&nachname=Schmidt&wohnort=Hamburg

Diese Adresse könnten wir bereits beim Starten eines XMLHttpRequest nutzen, d.h. sie als Parameter an die open-Methode übergeben:

xhr.open("GET", "beispiel.php?vorname=Alexandra&nachname=Schmidt&wohnort=Hamburg", true);

Spannend wird es aber erst, wenn es gilt, diese Adresse samt Query String dynamisch mit variablen Daten zusammenzusetzen, die z.B. der Benutzer eingegeben hat.

Nehmen wir an, es gibt im Dokument folgendes Formular mit den drei Feldern für Vorname, Nachname und Wohnort:

<form id="formular" action="beispiel.php">
<p><label>Vorname: <input type="text" name="vorname"></label></p>
<p><label>Nachname: <input type="text" name="nachname"></label></p>
<p><label>Wohnort: <input type="text" name="wohnort"></label></p>
<p><input type="submit" value="Absenden"></p>
</form>

Wenn JavaScript aktiviert ist, soll das Formular im Hintergrund per XMLHttpRequest abgesendet werden. Andernfalls erfolgt ein normales Absenden samt Auswechseln des gegenwärtig angezeigten Dokuments

Mittels traditionellem Event-Handling verknüpfen wir JavaScript-Logik mit dem Absenden des Formulares. Dazu nutzen wir das übliche mehrschrittige Schema:

window.onload = initFormularXHR;

function initFormularXHR() {
   document.getElementById("formular").onsubmit = formularSubmit;
}

function formularSubmit() {
   // Formular mit XMLHttpRequest absenden
}

Die Handler-Funktion formularSubmit muss nun folgendes leisten:

  1. Das Formular über das DOM ansprechen
  2. Den Query String mit den Formulareingaben zusammensetzen (man spricht von einer Serialisierung des Formulars)
  3. Einen XMLHttpRequest starten mit dem erzeugten Query String
  4. Das normale Absenden des Formulars unterbrechen, indem die Standardaktion abgebrochen wird

Am kompliziertesten ist der zweite Schritt, deshalb betrachten wir erst einmal das Gerüst mit den restlichen Aufgaben.

function formularSubmit() {
}

Helferfunktion für den zweiten Schritt, die Serialisierung des Formulars

  1. Um den Query String zusammenzusetzen, werden die Felder des Formulars durchlaufen
  2. Für jedes Feld dessen Namen und Wert auslesen und ein entsprechendes Paar erzeugen (name=wert)
  3. Dieses Paar an einen String anhängen, in dem der Query String zusammengesetzt wird

POST und der POST-Anfragekörper

Helferfunktion für XMLHttpRequest

Alternativtechniken zur Serverkommunikation

Neben XMLHttpRequest gibt es weitere JavaScript-Techniken, um im Hintergrund Daten mit dem Webserver auszutauschen. Strenggenommen wurde Ajax schon lange praktiziert, bevor sich XMLHttpRequest breiter Browserunterstützung erfreute. Aber erst mit XMLHttpRequest gelang Ajax der Durchbruch, weshalb beide Begriffe oft synonym verwendet werden. Dies allerdings zu Unrecht: Zwar ist XMLHttpRequest die vielseitigste, aber beileibe nicht immer die einfachste und geeignetste Methode, um Informationen mit dem Webserver auszutauschen. Alternativen sind unter anderem:

Iframes

In eingebettete Frames (Iframes) können Daten im Hintergrund geladen werden oder sie dienen als Ziel-Frame für Formulare. Ein Script im einbettenden Elterndokument kann auf diesen Frame und seine Inhalte zugreifen. Da der Website-Besucher nichts von diesen Vorgängen im Hintergrund mitbekommen muss, kann der Iframe kurzerhand mit CSS versteckt werden.

Bildobjekte

Schon seit den Anfängen von JavaScript existiert die Möglichkeit, per JavaScript Bildobjekte (Image-Objekte) zu erzeugen, um Grafiken in den Browsercache zu laden:

var bild = new Image();
bild.src = "beispiel.png";

Diese Image-Objekte lassen sich jedoch zweckentfremden, um eine Server-Anfrage (GET-Anfrage) zu starten. Es ist dabei nicht wichtig, ob der Server auch tatsächlich eine Bilddatei (oder überhaupt etwas) zurücksendet. Diese im Vergleich zu XMLHttpRequest sehr einfache Methode eignet sich für die Server-Aktionen, in denen man dem Server per GET-Anfrage etwas mitteilt, aber keine Antwort oder Bestätigung erwartet.

script-Elemente

Per JavaScript erzeugte script-Elemente, die Scripte selbst von fremden Servern einbinden können, die Daten in mit JavaScript direkt zugänglichen Formaten bereitstellen.

Besonderheiten: Cross-Site JSON und JSONP

Übertragungsformate

HTML / Nur-Text

JSON

[
   {
      "name" : "Harry S. Truman",
      "partei" : "Demokraten",
      "beginn" : 1945,
      "ende" : 1953
   },
   {
      "name" : "Dwight D. Eisenhower"
      "partei" : "Republikaner",
      "beginn" : 1953,
      "ende" : 1961
   },
   {
      "name" : "John F. Kennedy"
      "partei" : "Demokraten",
      "beginn" : 1961,
      "ende" : 1963
   }
]

XML

XMLHttpRequest hat seinen Namen daher, dass es ursprünglich dazu gedacht war, XML-Dokumente vom Server herunterzuladen. JavaScript kann anschließend mit verschiedenen Schnittstellen mit dem Dokument arbeiten. Das funktioniert zwar vorzüglich, allerdings ist das Extrahieren von Daten aus einem XML-Dokument über die DOM-Schnittstelle umständlich. In den meisten Fällen sollen die Daten nämlich im bestehenden HTML-Dokument angezeigt werden, sodass die XML-Daten in eine HTML-Struktur umgewandelt werden müssen.

Wenn Sie strukturierte Daten per XML übertragen wollen, müssen Sie ein eigenes Auszeichnungssprache nach den Regeln von XML entwerfen. Sie denken sich gewisse Elemente und Attribute und die grammatischen Regeln aus, wie diese zusammengesetzt werden können. Mithilfe dieser Datenstrukturen werden die zu übertragenden Informationen geordnet. Das zugehörige JavaScript kann diese Elemente ansprechen und die Werte der enthaltenen Textknoten bzw. Attribute auslesen.

Auch wenn Sie diese XML-Sprache nicht unbedingt in einer formalen Dokumentyp-Definition (DTD) niederschreiben müssen, ist das Entwerfen eines passenden XML-Derivates für für Ajax vergleichsweise aufwändig. Das Auslesen der Informationen im JavaScript erfordert nicht nur Kenntnisse des XML-DOM, sondern ist mit ziemlicher Schreibarbeit verbunden.

Wenn die serverseitigen Programme, die Ihre Website betreiben, bereits ein XML-Format als Speicher- oder Austauschformat verwendet und die damit ausgezeichneten Daten zum Client weitergeschickt werden sollen, bietet es sich auch für Ajax an. Andernfalls ist XML oftmals kompliziert und überdimensioniert.

<?xml version="1.0" encoding="UTF-8" ?>
<us-präsidenten>
   <präsident>
      <name>Harry S. Truman</name>
      <partei>Demokraten</partei>
      <beginn>1945</beginn>
      <ende>1953</ende>
   </präsident>
   <präsident>
      <name>Dwight D. Eisenhower</name>
      <partei>Republikaner</partei>
      <beginn>1953</beginn>
      <ende>1961</ende>
   </präsident>
   <präsident>
      <name>John F. Kennedy</name>
      <partei>Demokraten</partei>
      <beginn>1961</beginn>
      <ende>1963</ende>
   </präsident>
</us-präsidenten>

Ajax und Sicherheit

Same-Origin-Policy bei XMLHttpRequest

XMLHttpRequest unterliegt dem grundlegenden Sicherheitskonzept der Same-Origin-Policy. Das bedeutet, sie können mit XMLHttpRequest nur HTTP-Anfragen an den Webserver absenden, auf dem das Dokument liegt, in dessen Kontext das JavaScript ausgeführt wird.

Wenn Sie von einem Dokument auf example.org aus eine Anfrage an eine fremden Domain, z.B. example.net senden, dann wird der Browser das JavaScript mit einem Ausnahmefehler abbrechen.

// Script auf example.org
xhr.open("GET", "http://example.net/", true); // Fehler!

Sie können also nicht ohne weiteres HTTP-Anfragen an beliebige Adressen senden bzw. beliebige Adressen im Web auslesen – das verhindert die Same-Origin-Policy. Es gibt jedoch zahlreiche Möglichkeiten, mit JavaScript auf externe Dienste und Datenquellen im Web zuzugreifen und sogenannte Mashups zu erstellen. Diese verwenden nicht klassisches XMLHttpRequest, sondern nutzen alternative Techniken, bei denen andere Sicherheitsmodelle als Same-Origin-Policy greifen.

Cross-Site Ajax mit HTTP Access Control

Cross-Site, Cross-Origin oder auch Cross-Domain Ajax bezeichnet die Möglichkeit, eine HTTP-Anfrage an eine fremde Domain abzusenden und auf die Server-Antwort zuzugreifen. Derzeit etwickelt das Standardisierungsgremium W3C einen Ansatz, der Schnittstellen wie XMLHttpRequest einen domainübergreifenden Zugriff ermöglichen soll. Der Entwurf für die technische Spezifikation firmiert derzeit unter dem Namen Cross-Origin Resource Sharing (englisch für Herkunft-übergreifender Zugriff auf Ressourcen), ist aber bekannter unter dem Namen HTTP Access Control (englisch für HTTP-Zugriffskontrolle).

Mit diesem Modell lassen sich Ausnahmen von der Same-Origin-Policy festlegen, und zwar nach einem Einverständnis-Modell: Eine Website erlaubt einer bestimmten anderen Website, auf sie zuzugreifen. Gemäß dem obigen Beispiel kann die Zieldomain example.net einwilligen, dass Anfragen von Ursprungsdomain example.org erlaubt sind.

Diese Erlaubnis wird in einer bestimmten HTTP-Kopfzeile (Header) erteilt, die der Webserver unter example.net bei der Beantwortung einer Anfrage mitliefert. Um beispielsweise den domainübergreifenden Zugriff ausschließlich von Dokumenten auf http://example.org zu erlauben, sendet der Webserver die Kopfzeile:

Access-Control-Allow-Origin: http://example.org

Die Überprüfung der Erlaubnis nimmt der Browser vor: Wenn ein JavaScript von example.org eine Anfrage an example.net sendet, so prüft der Browser, ob die Antwort von example.net die besagten HTTP-Kopfzeile enthält. Falls ja, so kann das JavaScript die Serverantwort wie gewohnt verarbeiten, andernfalls wird das Script mit einem Ausnahmefehler abgebrochen. (???)

Dies ist nur ein vereinfachtes Beispiel, um einfache Anfragen zu erlauben. Die besagte Spezifikation sieht viel feinere Zugriffskontrolle vor. An dieser soll lediglich das Grundprinzip ersichtlich werden.

In der Praxis gibt es derzeit zwei Umsetzungen der W3C-Spezifikation: Mozilla Firefox erlaubt ab Version 3.5 den domainübergreifenden Zugriff mithilfe eines normalen XMLHttpRequest-Objekts. Der Internet Explorer ab Version 8 hat hingegen ein eigenes Objekt nur solche Zugriffe erfunden: XDomainRequest. Dessen Bedienung ähnelt der von XMLHttpRequest stark, es gibt im Detail allerdings einige Unterschiede, Eigenheiten und Verbesserungen.

Da beide Techniken noch recht neu und noch nicht breit unterstützt sind, sei hier nur am Rande darauf hingewiesen. …

Wie gesagt setzen diese Techniken die Same-Origin-Policy nicht vollständig außer Kraft, sondern ergänzen das Verbot des domainübergreifenden Zugriffs durch eine Ausnahme: Der Webserver auf der Zieldomain muss so konfiguriert sein, dass er sein Einverständnis zu dieser fremden Anfrage gibt.

Cross-Site Ajax mit Server-Proxy

Scripte einbinden und JSONP