WMP Webstandards

AJAX

zurück zur Übersicht

AJAX ist nicht etwa ein Putzmittel, es bedeutet Asynchronous Javascript and XML und ist der Name eines modernen Verfahren auf Internetseiten um externe Dateien zu laden.

Nach oben

Hintergrund

Der grosse Unterschied zwischen Desktop- und Webanwendungen liegt darin, dass bei jedem Schritt, wie z.B. ein Formular überprüfen, eine neue Seite geladen werden muss.

Dies kann etwa folgendermassen aussehen:

  1. Der Benutzer führt eine Aktion auf der Seite aus
  2. Diese Aktion führt dazu, dass eine neue Seite vom Server verlangt wird (HTTP-Anfrage)
  3. Der Server führt möglicherweise serverseitige Aktionen durch (z.B. Login überprüfen)
  4. Der Benutzer empfängt die HTML-Seite

Bei solchen klassischen Webanwendungen muss der Anwender nach einer Aktion warten, bis eine komplette neue HTML-Seite dynamisch generiert und übertragen wurde.

AJAX-basierte Webanwendungen sind im Unterschied zu klassischen Webanwendungen asynchron: Der Benutzer kann weitere Aktionen durchführen, während die HTTP-Anfrage noch übertragen bzw. bearbeitet sowie die Antwort übertragen wird. Durch AJAX bleibt der Benutzer immer auf einer Seite, die mittels JavaScript/DOM verändert wird.

  1. Der Benutzer führt eine Aktion auf der Seite aus
  2. Diese Aktion führt dazu, dass das JavaScript eine neue Datei vom Server verlangt (XMLHttpRequest)
  3. Der Server führt möglicherweise serverseitige Aktionen durch (z.B. Login überprüfen)
  4. Das JavaScript empfängt die Datei
  5. Der Inhalt der Seite wird per JavaScript/DOM mit den empfangenen Daten erweitert oder modifiziert

Durch AJAX sind Anwendungen im Internet möglich, die Desktop-Anwendungen ähneln. Der Benutzer erhält unmittelbar Rückmeldung auf seine Eingaben und es gibt keine Pausen, in denen die Anwendung nicht ansprechbar ist.

Nach oben

Beispiele im Internet

Es gibt bereits mehrere Webanwendungen, welche diese Methode verwenden. Als Beispiel möchte ich Google Suggest nehmen. Während man eintippt, wird eine Liste mit gleich beginnenden Wörter angezeigt.

Ein ähnliches Beispiel ist das LiveSearch von Bitflux, welches während dem eintippen die Resultate anzeigt.

Nach oben

Technik

AJAX ist im Grunde nur ein beschreibender Name. Die Technik bzw. das JavaScript Objekt heisst XMLHttpRequest. Wie der Name verrät, wird eine XML-Datei vom Server angefordert. Man kann aber auch jede mögliche Datei vom Server anfordern.

Wenn man eine XML-Datei anfordert, muss diese den MIME-Type "text/xml" haben, da man es ansonsten nicht per DOM auslesen kann, sondern nur als Text.

Aufbau eines Requests

  1. Ein neues XMLHttpRequest Objekt wird erstellt
  2. Diese Objekt fordert per GET oder POST eine Datei an
  3. Beim vollständigen Laden der Datei befindet sich der Inhalt der Datei im Objekt

Wir erstellen eine Funktion um das XMLHttpRequest Objekt zu erstellen. Dazu müssen wir eine Variable ausserhalb der Funktion haben, in der das Objekt gespeichert wird, damit das Objekt auch in anderen Funtionen existiert.

Da der Internet Explorer eine andere Methode hat um das Objekt zu erstellen, muss man zusätzlich überprüfen, ob der Browser jeweils die unterschiedlichen Objekte unterstützt. Falls das Objekt nicht unterstüzt wird, kann man eine Nachricht anzeigen, was wir in diesem Beispiel nicht machen werden.

var httpObject = false;

function getHttpObject() {
  if (window.XMLHttpRequest) {
    httpObject = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    httpObject = new ActiveXObject("Microsoft.XMLHTTP");
  } else {
    // Falls nicht unterstützt
    httpObject = false;
  }
}

Da das Objekt nun existiert bzw. initialisiert wurde, können wir eine Datei laden. Dazu erstellen wir eine simple XML-Datei, welche später auch per PHP o.ä. dynamisch erstellt werden kann.

<?xml version="1.0" encoding="UTF-8"?>
<benutzerliste>
  <benutzer>
    <name>Peter Meier</name>
    <email>peter@meier.com</email>
    <alter>46</alter>
    <beruf>Ingenieur</beruf>
  </benutzer>
  <benutzer>
    <name>Angela Schulz</name>
    <email>a.schulz@msn.com</email>
    <alter>39</alter>
    <beruf>Sekret&#228;rin</beruf>
  </benutzer>
  <benutzer>
    <name>Oliver Schmidt</name>
    <email>olivers@gmail.com</email>
    <alter>23</alter>
    <beruf>Student</beruf>
  </benutzer>
</benutzerliste>

Nun benötigen wir eine Funktion, welche die Datei lädt.

function loadXML() {
  httpObject.open("GET", "benutzerliste.xml", true);
  httpObject.onreadystatechange = function() {
    if (httpObject.readyState == 4) {
      if (httpObject.status == "200") {
        // Weitere Anweisungen bei erfolgreichem Öffnen
      } else {
        // Weitere Anweisungen bei missglücktem Öffnen
      }
    }
  }
  httpObject.send(null);
}

Die Funktion open() initialisiert eine Anfrage. Das erste Argument ist die Methode, GET oder POST, und das zweite die zu öffnende Datei. Das dritte Argument gibt an, ob der Vorgang asynchron oder synchron sein soll. Dieser Wert sollte immer true, was für asynchron steht, sein, damit der Benutzer weiterhin auf der Seite interagieren kann. Bei false bzw. synchronen Anfragen wird die Seite blockiert. Dies kann sogar dazu führen, dass der Browser bei einer grossen Datei nicht mehr antwortet.

onreadystatechange ist ein Eventhandler, ähnlich wie onload oder onmouseover. Wie der Name verrät, wird bei jedem Statuswechsel die dazu deklarierte Funktion aufgerufen. Es gibt 5 verschiedene Status. Der wichtigste ist der Status mit der Nummer 4, welcher angibt, dass die Datei vollständig geladen ist. Die Funktion die aufgerufen wird, überprüft das Objekt auf diesen Status. Falls dieser Status gleich 4 ist, wird geprüft, ob der Server den Code 200, wenn die Datei existiert und übertragen wurde, zurückgeliefert hat und nicht etwa 404 oder 500.

Nun wird die Anfrage noch per send() übertragen.

Alle Eigenschaften und Funktionen des XMLHttpRequest Objekts sind unter XUL Planet genau dokumentiert.

Wenn man mehrere Anfragen durchführt, ist es sinnvoll für jeden verschiedenen eine eigene Funktion zu schreiben, welche die Datei verarbeitet. Dazu habe ich die Funktion wie folgt erweitert:

function loadXML(url, funkt) {
  if (httpObject.readyState != 0) {
    http = getHttpObject();
  }
  httpObject.open("GET", url, true);
  httpObject.onreadystatechange = function() {
    if (httpObject.readyState == 4) {
      if (httpObject.status == "200") {
        eval(funkt + "()");
      } else {
        if (document.createElement) {
          var p = document.createElement("p"),
          pTxt = document.createTextNode("Ihr Browser unterst\u00FCtzt leider diese Seite nicht.");
          p.appendChild(pTxt);
          document.getElementById("container").appendChild(p);
        } else {
          alert("Ihr Browser unterst\u00FCtzt leider diese Seite nicht.");
        }
      }
    }
  }
  httpObject.send(null);
}

Nun kann man für jeden Aufruf der Funktion eine andere URL und Funktion, welche die Datei verarbeitet, angeben. Wenn man viele Anfragen in kurzen Zeitintervalen macht wie bei Google Suggest , kann es sein, dass das Objekt nicht korrekt arbeitet. Deshalb wird zusätzlich geprüft, ob das Objekt schon verwendet wurde und erstellt gegebenfalls ein neues. Zusätzlich habe ich eine Fehlerausgabe eingebaut.

Der Aufruf sieht nun so aus:

loadXML("benutzerliste.xml", "verarbeiten")

Beim Laden der Datei wird die Funktion verarbeiten() aufgerufen, welche wir noch erstellen müssen.

Diese Funktion soll die Namen aller Benutzer, welche in der XML-Datei vorhanden sind, per DOM in einer Tabelle anzeigen.

function verarbeiten() {
  var xml = httpObject.responseXML;
  var benutzerliste = xml.getElementsByTagName("benutzer"),
  tabelle = document.createElement("table"),
  benutzer, tr, td, j, i = 0,
  txt = new Array("Name","Alter","Email","Beruf"),
  tag = new Array("name","alter","email","beruf"),
  container = document.getElementById("container");
  for (i; i<=benutzerliste.length; i++) {
    benutzer = (i>0) ? benutzerliste[i-1] : false;
    tr = document.createElement("tr");
    for (j=0; j<4; j++) {
      td = (i==0) ? document.createElement("th") :
document.createElement("td");
      tdTxt = (i==0) ? document.createTextNode(txt[j]) :
document.createTextNode(benutzer.getElementsByTagName(tag[j])[0].firstChild.nodeValue);
      td.appendChild(tdTxt);
      tr.appendChild(td);
    }
    tabelle.appendChild(tr);
  }
  container.removeChild(container.getElementsByTagName("a")[0]);
  container.appendChild(tabelle);
}

Die empfangene XML-Datei ist nun in Textform unter der Eigenschaft responseText oder in einer DOM Struktur unter der Eigenschaft responseXML im Objekt enthalten. Wir arbeiten mit letzterer Eigenschaft, da es sich ideal für unsere Beispieldatei eignet.

Diese Funktion erzeugt eine Tabelle mit vier Zeilen. Die erste Zeile wird mit Zellenüberschriften versehen. Die restlichen werden mit den Daten gefüllt. Danach löscht sie den Link im HTML-Element und fügt die Tabelle ein. Unsere HTML-Datei muss also folgendes enthalten:

<div id="container">
  <a href="#" id="link">Benutzerliste anzeigen</a>
</div>

Beim Klicken auf den Link wird der ganze Vorgang vom Laden und Tabellen Erzeugen gestartet. Den Link fügen wir aber erst per DOM ein, wenn wir geprüft haben, ob der Browser das XMLHttpRequest Objekt unterstützt.

Eine Seite darf niemals nur von XMLHttpRequest abhängen, da es Browser und Geräte gibt, welche dies nicht unterstützen. Bei diesem Beispiel sollte man eigentlich auch eine Serverseitige Lösung anbieten. Ich verzichte in diesem Beispiel darauf. Daher müssen wir dem Benutzer melden, falls er es nicht unterstützt oder JavaScript deaktiviert hat.

Dazu fügen wir ein noscript Element ein, welches nur angezeigt wird, wenn JS deaktiviert ist:

<noscript>
  <p>Sie <strong>müssen</strong> JavaScript aktivieren, um diese Seite zu verwenden.</p>
</noscript>

Jetzt muss noch gleiches getan werden, falls das Objekt nicht unterstützt wird, was bei älteren Browsern der Fall ist:

var httpObject = false;

function getHttpObject() {
  if (window.XMLHttpRequest) {
    httpObject = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    httpObject = new ActiveXObject("Microsoft.XMLHTTP");
  } else {
    if (document.createElement) {
      var p = document.createElement("p"),
      pTxt = document.createTextNode("Ihr Browser unterst\u00FCtzt diese Seite leider nicht.");
      p.appendChild(pTxt);
      document.getElementById("container").appendChild(p);
    } else {
      alert("Ihr Browser unterst\u00FCtzt leider diese Seite nicht.");
    }
    httpObject = false;
  }
}

Nun fehlt uns nur noch die Funktion, welche beim Laden der Seite aufgerufen wird. Diese soll das Objekt und den Link erzeugen.

window.onload = function() {
  getHttpObject();
  if (httpObject != false) {
    var a = document.createElement("a"),
    aTxt = document.createTextNode("Benutzerliste laden");
    a.appendChild(aTxt);
    a.setAttribute("href", "#");
    a.onclick = function() {
      loadXML("benutzerliste.xml", "verarbeiten");
    }
    document.getElementById("container").appendChild(a);
  }
}

Nun sollte die JavaScript-Datei folgendermassen aussehen:

var httpObject = false;

function getHttpObject() {
  if (window.XMLHttpRequest) {
    httpObject = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    httpObject = new ActiveXObject("Microsoft.XMLHTTP");
  } else {
    if (document.createElement) {
      var p = document.createElement("p"),
      pTxt = document.createTextNode("Ihr Browser unterst\u00FCtzt leider diese Seite nicht.");
      p.appendChild(pTxt);
      document.getElementById("container").appendChild(p);
    } else {
      alert("Ihr Browser unterst\u00FCtzt leider diese Seite nicht.");
    }
    httpObject = false;
  }
}

function loadXML(url, funkt) {
  if (httpObject.readyState != 0) {
    http = getHttpObject();
  }
  httpObject.open("GET", url, true);
  httpObject.onreadystatechange = function() {
    if (httpObject.readyState == 4) {
      if (httpObject.status == "200") {
        eval(funkt + "()");
      } else {
        var p = document.createElement("p"),
        pTxt = document.createTextNode("Ihr Browser unterst\u00FCtzt leider diese Seite nicht.");
        p.appendChild(pTxt);
        document.getElementById("container").appendChild(p);
      }
    }
  }
  httpObject.send(null);
}

function verarbeiten() {
  var xml = httpObject.responseXML;
  var benutzerliste = xml.getElementsByTagName("benutzer"),
  tabelle = document.createElement("table"),
  benutzer, tr, td, j, i = 0,
  txt = new Array("Name","Alter","Email","Beruf"),
  tag = new Array("name","alter","email","beruf"),
  container = document.getElementById("container");
  for (i; i<=benutzerliste.length; i++) {
    benutzer = (i>0) ? benutzerliste[i-1] : false;
    tr = document.createElement("tr");
    for (j=0; j<4; j++) {
      td = (i==0) ? document.createElement("th") :
document.createElement("td");
      tdTxt = (i==0) ? document.createTextNode(txt[j]) :
document.createTextNode(benutzer.getElementsByTagName(tag[j])[0].firstChild.nodeValue);
      td.appendChild(tdTxt);
      tr.appendChild(td);
    }
    tabelle.appendChild(tr);
  }
  container.removeChild(container.getElementsByTagName("a")[0]);
  container.appendChild(tabelle);
}

window.onload = function() {
  getHttpObject();
  if (httpObject) {
    var a = document.createElement("a"),
    aTxt = document.createTextNode("Benutzerliste laden");
    a.appendChild(aTxt);
    a.setAttribute("href", "#");
    a.onclick = function() {
      loadXML("benutzerliste.xml", "verarbeiten");
    }
    document.getElementById("container").appendChild(a);
  }
}

Der body-Bereich der Seite sollte wie folgt aussehen:

<div id="container">
  <noscript>
    <p>Sie <strong>müssen</strong> JavaScript aktivieren, um diese Seite zu verwenden.</p>
  </noscript>
</div>

Hier kann man sich das Resultat ansehen.

Nach oben

Einsatzgebiete

Mit AJAX können wir dem Besucher das Leben erleichtern.

Registrierungsformulare

Sie kennen sicherlich das Problem der Benutzernamenwahl bei einer Registrierung. Sie geben diesen ein, z.B. "Thomas", das gewünschte Passwort etc. und übermitteln das Formular. Eine neue Seite erscheint und meldet Ihnen, dass der Name bereits in Verwendung ist. Meistens wird dabei auch das Passwortfeld gelöscht.

Viel benutzerfreundlicher wäre es, wenn man das Textfeld des Benutzernamens verlässt, automatisch per AJAX überprüft wird, ob der Name noch frei ist.

Online Einkaufen

Bei online Einkäufe muss bei jedem zum Warenkorb hinzugefügte Produkt die Seite neu geladen werden. Nach meiner Erfahrung ist online Einkaufen wegen diesem und anderen Faktoren eine ziemlich zeitkostende und lästige Angelegenheit.

Dies wäre mit AJAX Vergangenheit und ein neues Einkauferlebnis wäre geschaffen.

Nach oben

Fazit

Mit AJAX kann man eine Seite benutzerfreundlicher und moderner bzw. dynamischer gestalten. Es ist sicherlich eine sehr nützliche Erweiterung, von der eine Seite nicht abhängen darf. Das heisst man sollte immer eine Serverseitige Lösung anbieten, damit keine Benutzer ausgeschlossen werden.

Nach oben

Links zum Thema

Kommentare







22.06.2005 Löschen

pberndt

Schöner Artikel! Eine Ergänzung um die (bisher nur von Firefox unterstützten) Möglichkeit des Streamings fänd ich noch nett, Chats lassen sich z.B. so mit einer persistenten Serververbindung realisieren.

23.07.2005 Löschen

pmc

Aber es ist nicht möglich, mit javascript Werte in eine xml datei zu schreiben, oder?

03.08.2005 Löschen

fphilipe

@pmc:
Nein. Was man machen kann ist eine PHP o.ä. Datei aufrufen, welche dies macht und die Daten per GET oder POST übermitteln.

27.09.2005 Löschen

migg

Man sollte allerdings bei der Anwendung von AJAX beachten, dass der Back-Button des Browsers nichts von den Aktionen mitbekommt! Bei den beiden genannten Einsatzgebieten ist das sicherlich sogar ein Vorteil gegenüber der Realisierung ohne AJAX. Allerdings bei dem Beispiel ist es ein Nachteil, da die Benutzerliste sich nicht mehr direkt verlinken lässt, und auch der Back-Button nicht dazu führt dass die Liste wieder verschwindet (was man erwarten würde). Da der Back-Button der meistgenutzte Button ist, sollte man sich die Vor- und Nachteile vorher genau überlegen.

09.01.2007 Löschen

ruben93

Dafür gibts aber auch bugfixer. einfach mal bei developer.mozilla.org vorbeischaun, da gibts es artikel dazu

19.03.2010 Löschen

dsv

svd

Von fphilipe. Letzte Änderung am 07.12.2005