diff --git a/ajax.html b/ajax.html index b669181..4be9d1a 100644 --- a/ajax.html +++ b/ajax.html @@ -3,8 +3,9 @@
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.
- +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.
@@ -87,7 +88,7 @@Vom Benutzer veränderbare Daten (z.B. ein Kundenprofil oder Einstellungen) werden üblicherweise in zwei Ansichten angezeigt: +
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.
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 Objektes 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.
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.
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.
Die Absenden einer Anfrage mittels XMLHttpRequest umfasst vier grundlegende Schritte:
- +var xhr = new XMLHttpRequest();@@ -126,14 +127,14 @@
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. ...
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.
xhr.open("GET", "beispiel.html", true);@@ -145,7 +146,7 @@
Im obigen Beispielcode wird eine asynchrone GET-Anfrage an die Adresse beispiel.html abgesendet.
xhr.send(null);@@ -155,7 +156,7 @@
Vor dem Absenden eines asynchronen XMLHttpRequest haben wir eine Handler-Funktion namens xhrReadyStateHandler
registriert. Diese wird beispielhaft wie folgt notiert:
function xhrReadyStateHandler () { … @@ -187,7 +188,7 @@Anfrage-Status:
// Server-Antwort ist eingetroffen! }readyState
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.
responseCode
Diese kann Ihnen als Seitenbetreiber helfen, den Fehler zu beheben – für Ihre Seitenbesucher ist sie allerdings oftmals wenig hilfreich.
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.
responseText
ins Dokument schreibenEine 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 () { @@ -324,7 +325,7 @@}
GET
und der Query String
...
Helferfunktion für den zweiten Schritt, die Serialisierung des Formulars
- +name=wert
)GET
und der Query Stringscript
-Elemente...
...
@@ -396,7 +397,7 @@...
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.
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, 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 (engl. für Herkunft-übergreifender Zugriff auf Ressourcen), ist aber bekannter unter dem Namen HTTP Access Control (engl. für HTTP-Zugriffskontrolle).
@@ -455,12 +456,12 @@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.
currentStyle
currentStyle
liefert meist ein ähnliches Ergebnis wie das standardisierte getComputedStyle
. In manchen Fällen gibt es jedoch Abweichungen, etwa im obigen Beispiel beim Auslesen des width
-Wertes. currentStyle.width
gibt auto
zurück, wenn dem Element keine explizite Breite zugewiesen wurde. Für das browserübergreifende Auslesen der Box-Größe eignen sich stattdessen die Eigenschaft offsetWidth/offsetHeight
sowie clientWidth/clientHeight
.
Dies funktioniert zwar browserübergreifend, allerdings ist das Ergebnis unterschiedlich: Ältere Internet Explorer, in welchen nur currentStyle
zur Verfügung steht, geben für die font-size
-Eigenschaft einen pt
-Wert zurück, andere Browser einen px
-Wert. Mit diesen Browserunterschieden müssen Sie rechnen, sie lassen sich nicht einfach vereinheitlichen.
Mit dem script
-Element können Sie sowohl Scripte im Dokumentkopf als auch im Dokumentkörper einbetten. Die Ausführung des Scriptcodes läuft nach gewissen Regeln ab, die wir im folgenden betrachten.
Wenn der Browser das HTML-Dokument vom Webserver empfängt, beginnt er sofort damit, den Quellcode zu verarbeiten und in eine interne Speicherstruktur, das Document Object Model (DOM) zu überführen. Das dafür zuständige Modul im Browser nennt sich Parser und der Verarbeitungsvorgang Parsen.
Sobald der Parser auf ein script
-Element trifft, wird das Parsing des HTML-Dokuments angehalten und der JavaScript-Code innerhalb des script
-Elements ausgeführt. Dasselbe gilt für externe JavaScript-Dateien: Der HTML-Parser stoppt, lädt die externe JavaScript-Datei vom Webserver, führt den JavaScript-Code aus und fährt erst dann mit der Verarbeitung des restlichen HTML-Quellcodes fort.
Das Beispiel bindet drei Scripte ein, die ersten beiden als externe Dateien, das dritte direkt im HTML-Code. Da der Browser die Scripte in der Reihenfolge ihrer Einbindung ausführt, können spätere Scripte die Objekte, Funktionen und Variablen nutzen, die die vorher eingebundenen Scripte definiert haben. Im Beispiel wird zuerst grundlagenscript.js eingebunden, heruntergeladen und ausgeführt. Das darauffolgende Script aus der Datei aufbauscript.js kann die darin notierten Funktionen nutzen. Schließlich kann das dritte Script eine Funktion nutzen, die in aufbauscript.js definiert wurde.
Dass der Webbrowser die eingebundenen Scripte nicht erst nach, sondern bereits während dem Einlesen des HTML-Codes ausführt, hat Vor- und Nachteile. Einerseits werden Scripte so schnell wie möglich ausgeführt und es ist garantiert, dass ein externes Script ausgeführt wird, bevor ein nachfolgendes internes Script abgearbeitet wird. Andererseits verlangsamt sich der Seitenaufbau, wenn große externe Scriptdateien vom Webserver heruntergeladen werden.
Dieser Nachteil kann dadurch umgangen werden, alle script
-Elemente in der notwendigen Reihenfolge am Dokument-Ende zu platzieren anstatt wie üblich in den Dokumentkopf. Aus Gründen der kürzeren Ladezeit und des schnelleren Aufbau des Dokumentes wird dies immer öfter empfohlen. Es setzt allerdings eine bestimmte Arbeitsweise voraus. Im Abschnitt Ereignisbasierte Scripte werden wir eine Methode kennenlernen, bei die Scripte die Hauptarbeit erst verrichten, wenn das Dokument vollständig geladen wurde.
document.write
ergänzenMit der Methode document.write
kann ein Script schon während dem Laden das Dokument direkt beeinflussen und einige Weichen stellen. document.write
nimmt HTML-Code in einem JavaScript-String entgegen und fügt diesen an der Stelle ins Dokument ein, an denen das zugehörige script
-Element steht.
document.writedocument.write
ist beim »Unobtrusive JavaScript« nur sehr selten sinnvoll. Inhalte, die nur bei aktiviertem JavaScript sichtbar sein sollen, da sie auf JavaScript-Funktionalität beruhen, sollten Sie ohne document.write
dynamisch ins Dokument einfügen. Die dazu nötigen Techniken werden wir noch kennenlernen.
Der Anwendungsbereich von document.write
wird oftmals missverstanden. Wir haben hier den einen von zwei möglichen Anwendungsfällen kennenlernt: Das Ergänzen eines Dokuments noch während der Browser den HTML-Code einliest. Wenn document.write
jedoch nach dem vollständigen Einlesen de HTML-Codes aufgerufen wird, hat die Methode einen ganz anderen Effekt und eignet sich nicht dazu, das vorhandene Dokument via JavaScript zu ändern.
script
-ElementWie Sie vielleicht wissen, ist die häufigste Aufgabe von JavaScripten der Zugriff auf das Dokument über die DOM-Schnittstelle, die die Elemente und deren Textinhalte als Knotenobjekte zugänglich macht. Da ein Script mitten im Parsing-Prozess, also während des Einlesens des HTML-Dokuments ausgeführt wird, hat es zu diesem Zeitpunkt noch nicht Zugriff auf den gesamten DOM-Elementenbaum. Stattdessen kann es nur auf einen Teil-Baum zugreifen, nämlich auf die Elemente, die vor dem zugehörigen script
-Element liegen und somit vom Parser bereits verarbeitet wurden.
In dieser Funktion wird das Zielelement des Ereignisses angesprochen und dessen Elementname überprüft. Wenn ein a
-Element geklickt wurde, muss es sich um einen Link auf ein Bild handeln und das Vollbild soll eingeblendet werden.
Event-Capturing ist nur unter Verwendung der standardisierten Methode addEventListener
möglich. Das traditionelle Event-Handling mit seinem Schema element.onevent = handlerfunktion
registriert den Handler immer für die Bubbling-Phase. Dasselbe gilt für das Microsoft-Modell mit attachEvent
. Für Internet Explorer vor Version 9, welche addEventListener
nicht unterstützen, müssen Sie daher gegebenenfalls eine Alternative ohne Event-Capturing bereitstellen.
Um Event-Handler für die Capturing-Phase zu registrieren, nutzen Sie wie gewohnt addEventListener
, setzen jedoch den dritten Boolean-Parameter auf true
:
document.addEventListener("focus", captureHandler, true);- +
Die Vorteile des Capturings liegen also darin, insbesondere nicht aufsteigende Ereignisse bei einem höherliegenden Element zu verarbeiten.
Folgende Ereignisse beispielsweise steigen nicht auf: