Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einleitung
2 Webbrowser
3 JavaScript einbauen
4 Programmieren mit JavaScript
5 Datum
6 Zufall
7 Browserinformationen
8 Fenster
9 Formulare
10 Frames und Iframes
11 Grafiken
12 Cookies
13 Objekte und Arrays
14 Ereignisbehandlung
15 Fehlerbehandlung
16 DOM
17 DHTML und CSS
18 AJAX
19 XML
20 Web Services
21 AJAX-Frameworks
22 Plugins
23 Multimedia
24 Java
25 Serverseitige Technologien
26 JavaScript goes .NET
27 Code schützen
28 Top Secret: Passwortschutz
29 Signierte Skripte
30 JavaScript und Sicherheit
31 Navigation
32 Warenkorb
33 JavaScript-Bibliotheken
34 Referenz
35 Quellen im Web
36 Die Zukunft von JavaScript
Index
Download: - ZIP, ca. 9,2 MB
Buch bestellen
Ihre Meinung?
<< zurück | Galileo Computing / <openbook> / JavaScript und AJAX | vor >>
JavaScript und AJAX von Christian Wenz Das umfassende Handbuch
JavaScript und AJAX 839 S., mit DVD, 39,90 Euro Galileo Computing ISBN 3-89842-859-1 |
| Kapitel 18 AJAX
|  | 18.1 AJAX-Beispiele
|  | 18.2 AJAX-Technik
|  |  | 18.2.1 HTTP-Anfragen senden und auswerten
|  |  | 18.2.2 Parameter senden
|  |  | 18.2.3 Mit komplexen Daten arbeiten – JSON
|  |  | 18.2.4 Anfragen abbrechen
|  |  | 18.2.5 Weitere Möglichkeiten
|  | 18.3 AJAX-Probleme (und -Lösungen)
|  |  | 18.3.1 Bookmarks
|  |  | 18.3.2 Zurück-Schaltfläche
## 18.3 AJAX-Probleme (und -Lösungen) So weit die notwendigen AJAX-Grundlagen, zumindest in Hinblick auf das XMLHttpRequest-Objekt. Alles Weitere rund um AJAX hat mit dem Objekt nur noch wenig zu tun, sondern setzt auf andere Techniken: CSS, DOM, DHTML. Allerdings sollen abschließend noch einige Nachteile von AJAX geschildert und Lösungsmöglichkeiten skizziert werden. Der größte Nachteil ist ja offensichtlich: AJAX verlangt JavaScript, ohne JavaScript kein AJAX. Je nach Statistik ist der Wert ein anderer, aber etwa 90 % der Anwender können AJAX-Effekte verwenden, die restlichen 10 % haben Browser ohne oder mit deaktiviertem JavaScript. Allein aus diesem Grund sollten AJAX-Effekte immer nur ein schickes Beiwerk sein, aber nie die komplette Anwendungslogik tragen. Außer natürlich, Sie können auf die 10 % der Nutzer verzichten. Aber es gibt auch noch andere potenzielle Probleme, die es zu umschiffen gilt. Ausgangsbasis ist ein leicht modifiziertes Beispiel von vorhin: Erneut werden JSON-Daten vom Server geladen und als Liste ausgegeben, aber erst auf Knopfdruck (beziehungsweise Klick auf eine Schaltfläche):
    <html>
    <head>
    <title>AJAX</title>
    <script type="text/javascript"><!--
    var http = null;
    function ladeJSON() {
       if (window.XMLHttpRequest) {
          http = new XMLHttpRequest();
       } else if (window.ActiveXObject) {
          http = new ActiveXObject("Microsoft.XMLHTTP");
       }
       if (http != null) {
          http.open("GET", "json.txt", true);
          http.onreadystatechange = ausgeben;
          http.send(null);
       }
    }

    function ausgeben() {
       if (http.readyState == 4) {
          var daten = http.responseText;
          daten = eval("(" + daten + ")");

          var liste = document.getElementById("Liste");
          for (var i = 0; i < daten.length; i++) {
             var link = daten[i];
             var li = document.createElement("li");
             var a = document.createElement("a");
             a.setAttribute("href", link.url);
             var txt = document.createTextNode(link.text);
             a.appendChild(txt);
             li.appendChild(a);
             liste.appendChild(li);
          }
       }
    }
    //--></script>
    </head>
    <body>
    <ul id="Liste"></ul>
    <form>
       <input type="button" value="Laden"
          onclick="ladeJSON();" />
    </form>
    </body>
    </html>
Diese Anwendung hat ein paar mögliche Nachteile, um die wir uns im Folgenden kümmern werden. Und auch wenn das Beispiel sehr einfach ist – auch komplexere Praxis-AJAX-Anwendungen haben in der Regel die Nachteile, die im Folgenden besprochen werden. Unsere Gegenmittel wirken dann aber auch dort. ### 18.3.1 Bookmarks Nachteil Nummer 1: Das Beispiel erlaubt keine Bookmarks (sie heißen Favoriten im Internet Explorer und Lesezeichen in Mozilla-Browsern). Der ursprüngliche Zustand (leere Liste) der Seite hat dieselbe URL wie der Endzustand der Seite (gefüllte Liste, nach Klick auf die Schaltfläche). Dazu gibt es zwei verschiedene Ansätze. Häufig wird hier der Begriff Permalink gebraucht: Das sind permanente Links, also solche, die sich nicht ändern. Nach jedem Zustandswechsel müssen Sie einen neuen Permalink erzeugen und somit Ihre Benutzer darauf hinweisen, diesen Link als Bookmark abzulegen. So weit die allgemeinen Ausführungen für so gut wie alle AJAX-Anwendungen – jetzt kommen wir zur spezifischen Implementierung für das vorliegende Beispiel. Andere AJAX-Szenarien erfordern andere Implementierungen, aber das grundlegende Prinzip bleibt absolut dasselbe. Die Beispielanwendung kennt nur zwei Zustände, weswegen die Implementierung recht übersichtlich ist. Per URL muss der Zustand mitgeteilt werden, beispielsweise im Query-String. So könnte der Query-String ?geladen bedeuten, dass der Zustand »Liste geladen« gewünscht wird; der Standardzustand ist »Liste nicht geladen«. Wenn also die HTML-Seite geladen wird, wirft JavaScript einen Blick auf location.search und sucht nach dem String »geladen«. Wird JavaScript fündig, wechselt die Anwendung in den anderen Zustand und lädt die JSON-Daten vom Server:
    window.onload = function() {
       if (location.search.indexOf("geladen") > –1) {
          ladeJSON();
       }
    }
Nun fehlt nur noch der Permalink:
    <a id="Link" href="bookmark1.html">Permalink</a>
Nach dem Laden der Liste, also innerhalb der Funktion ausgeben(), muss dieser Link entsprechend angepasst werden:
    document.getElementById("Link").setAttribute(
       "href", "bookmark1.html?geladen");
Hier der komplette Code:
    <html>
    <head>
    <title>AJAX</title>
    <script type="text/javascript"><!--
    var http = null;

    window.onload = function() {
       if (location.search.indexOf("geladen") > –1) {
          ladeJSON();
       }
    }
    function ladeJSON() {
       if (window.XMLHttpRequest) {
          http = new XMLHttpRequest();
       } else if (window.ActiveXObject) {
          http = new ActiveXObject("Microsoft.XMLHTTP");
       }
       if (http != null) {
          http.open("GET", "json.txt", true);
          http.onreadystatechange = ausgeben;
          http.send(null);
       }
    }

    function ausgeben() {
       if (http.readyState == 4) {
          var daten = http.responseText;
          daten = eval("(" + daten + ")");

          var liste = document.getElementById("Liste");
          for (var i = 0; i < daten.length; i++) {
             var link = daten[i];
             var li = document.createElement("li");
             var a = document.createElement("a");
             a.setAttribute("href", link.url);
             var txt = document.createTextNode(link.text);
             a.appendChild(txt);
             li.appendChild(a);
             liste.appendChild(li);
          }

          document.getElementById("Link").setAttribute(
             "href", "bookmark1.html?geladen");
       }
    }
    //--></script>
    </head>
    <body>
    <ul id="Liste"></ul>
    <a id="Link" href="bookmark1.html">Permalink</a>
    <form>
       <input type="button" value="Laden"
          onclick="ladeJSON();" />
    </form>
    </body>
    </html>
Hier klicken, um das Bild zu Vergrößern Abbildung 18.10 Nach dem Laden der Daten ist der Permalink angepasst. Doch dieses Vorgehen erfordert immer noch ein manuelles Eingreifen des Nutzers, was vor allem für unerfahrene Surfer etwas ungewohnt ist. Deswegen wäre es schön, wenn sich die URL bei jedem Statuswechsel selbst aktualisieren würde. Eine Veränderung des Query-Strings allerdings würde die Seite erneut vom Server laden; dieses Verfahren scheidet damit aus. Der einzige Ausweg: Setzen Sie eine Textmarke (location.hash)! Das lädt die Seite nicht neu, aber die URL gibt dann genau den Status an. Hier der veränderte Code, ohne extra Permalink:
    <html>
    <head>
    <title>AJAX</title>
    <script type="text/javascript"><!--
    var http = null;

    window.onload = function() {
       if (location.hash.indexOf("geladen") > –1) {
          ladeJSON();
       }
    }
    function ladeJSON() {
       if (window.XMLHttpRequest) {
          http = new XMLHttpRequest();
       } else if (window.ActiveXObject) {
          http = new ActiveXObject("Microsoft.XMLHTTP");
       }
       if (http != null) {
          http.open("GET", "json.txt", true);
          http.onreadystatechange = ausgeben;
          http.send(null);
       }
    }
    function ausgeben() {
       if (http.readyState == 4) {
          var daten = http.responseText;
          daten = eval("(" + daten + ")");

          var liste = document.getElementById("Liste");
          for (var i = 0; i < daten.length; i++) {
             var link = daten[i];
             var li = document.createElement("li");
             var a = document.createElement("a");
             a.setAttribute("href", link.url);
             var txt = document.createTextNode(link.text);
             a.appendChild(txt);
             li.appendChild(a);
             liste.appendChild(li);
          }

          location.hash = "geladen";
       }
    }
    //--></script>
    </head>
    <body>
    <ul id="Liste"></ul>
    <form>
       <input type="button" value="Laden"
          onclick="ladeJSON();" />
    </form>
    </body>
    </html>
Hier klicken, um das Bild zu Vergrößern Abbildung 18.11 Die URL der Seite passt sich jetzt automatisch an. ### 18.3.2 Zurück-Schaltfläche Ganz perfekt ist die Anwendung aber trotz Bookmark-Unterstützung immer noch nicht. Achten Sie im Browser darauf, wie sich die Zurück- und Vorwärts-Schaltflächen verhalten. Im Internet Explorer passiert gar nichts, die Schaltflächen bleiben ausgegraut. Das Laden der Liste erzeugt also keinen Eintrag in der Browser-History (Verlauf im Internet Explorer, Chronik im Firefox). In Mozilla-Browsern ist das Verhalten ein wenig anders: Die Veränderung von location.hash erzeugt einen History-Eintrag. Wenn Sie also die Datei bookmark2.html laden und dann auf die Schaltfläche klicken, ändert sich die URL auf bookmark2.html#geladen; die Zurück-Schaltfläche springt dann zur URL bookmark2.html zurück. Allerdings wird dadurch die Liste nicht gelöscht. Zunächst zum Mozilla-Problem. Beim Laden der Seite muss jetzt eben auch der Zustand »Liste nicht geladen« abgefangen werden. Ein erster Ansatz sieht so aus:
    window.onload = function() {
       if (location.hash.indexOf("geladen") > –1) {
          ladeJSON();
       } else {
          document.getElementById("Liste").innerHTML = "";
       }
    }
Allerdings scheitert das am Caching-Mechanismus der Browser: Das load-Ereignis findet unter Umständen nicht statt, wenn per Browser-Schaltfläche vor- und zurückgesprungen wird. Deswegen müssen Sie per setInterval() oder setTimeout() periodisch location.hash überprüfen. Bei einer Veränderung müssen Sie aktiv werden. Hier ein Vorschlag, wie das implementiert werden kann: Der aktuelle Zustand (sprich, location.hash) wird in einer globalen Variablen abgespeichert:
    var zustand = location.hash;
Die Funktion laden() wird insofern modifiziert, als dass zustand entsprechend angepasst wird:
    var laden = function() {
       if (location.hash.indexOf("geladen") > –1) {
          ladeJSON();
       } else {
          document.getElementById("Liste").innerHTML = "";
       }
       zustand = location.hash;
    }
Die Funktion laden() muss nun mehrfach ausgeführt werden: zum einen direkt beim Laden der Seite, zum anderen aber auch danach regelmäßig, um etwaige Änderungen an location.hash zu erfassen. Ein Wert von 500 Millisekunden erscheint geeignet:
    window.onload = laden;
    window.setInterval(function() {
       if (zustand != location.hash) {
          laden();
       }
    }, 500);
Ein wichtiger Punkt fehlt noch: Wenn die Funktion ausgeben() den Wert von location.hash neu setzt, muss auch die Variable zustand angepasst werden, denn ansonsten würde der JavaScript-Code die Liste zweimal laden. Hier der vorläufige Zwischenstand des Beispiels:
    <html>
    <head>
    <title>AJAX</title>
    <script type="text/javascript"><!--
    var http = null;
    var zustand = location.hash;

    var laden = function() {
       if (location.hash.indexOf("geladen") > –1) {
          ladeJSON();
       } else {
          document.getElementById("Liste").innerHTML = "";
       }
       zustand = location.hash;
    }
    window.onload = laden;
    window.setInterval(function() {
       if (zustand != location.hash) {
          laden();
       }
    }, 500);
    function ladeJSON() {
       if (window.XMLHttpRequest) {
          http = new XMLHttpRequest();
       } else if (window.ActiveXObject) {
          http = new ActiveXObject("Microsoft.XMLHTTP");
       }
       if (http != null) {
          http.open("GET", "json.txt", true);
          http.onreadystatechange = ausgeben;
          http.send(null);
       }
    }

    function ausgeben() {
       if (http.readyState == 4) {
          var daten = http.responseText;
          daten = eval("(" + daten + ")");

          var liste = document.getElementById("Liste");
          for (var i = 0; i < daten.length; i++) {
             var link = daten[i];
             var li = document.createElement("li");
             var a = document.createElement("a");
             a.setAttribute("href", link.url);
             var txt = document.createTextNode(link.text);
             a.appendChild(txt);
             li.appendChild(a);
             liste.appendChild(li);
          }

          location.hash = "geladen";
          zustand = location.hash;
       }
    }
    //--></script>
    </head>
    <body>
    <ul id="Liste"></ul>
    <form>
       <input type="button" value="Laden"
          onclick="ladeJSON();" />
    </form>
    </body>
    </html>
In Mozilla-Browsern funktionieren jetzt die Navigationsschaltflächen wieder, auch der Opera kooperiert; aber im Internet Explorer tut sich immer noch nichts. Das hat einen Grund: Dort erzeugen nur tatsächliche HTTP-Anfragen einen neuen Eintrag in der Verlaufsliste. Also bedarf es eines speziellen Tricks: In einem unsichtbaren Frame oder Iframe (siehe Kapitel 10) wird jedes Mal eine neue Datei geladen, wenn sich der Zustand der Seite ändert. Hier zunächst der Iframe, der per CSS-Anweisung unsichtbar gemacht wird:
    <iframe
       src="history.html"
       name="historyframe"
       style="display:none"></iframe>
Bei jedem Zustandswechsel muss die Datei history.html neu geladen werden (und den aktuellen Zustand im Query-String erhalten). Dies kann in der Funktion laden() realisiert werden. Stimmt der aktuelle Zustand mit dem Query-String des versteckten Iframes überein? Falls nicht, muss sich das ändern:
    with (window.frames["historyframe"].window.location) {
       if (search != "?" + escape(zustand.substring(1)) &&
           window.ActiveXObject) {
          search = "?" + escape(zustand.substring(1));
       }
    }
Im Frame selbst wird ein Blick zurück geworfen: Ist der Zustand aus der Hauptseite (top.zustand) identisch mit dem Zustand, der an den Iframe übergeben wurde? Denn wenn der Ifame neu geladen wird, muss gegebenenfalls die Hauptseite aktualisiert werden. Deswegen hier der Code der Datei history.html:
    <script type="text/javascript"><!--
    if (window.ActiveXObject && // ist es ein IE?
       "?" + top.zustand.substring(1) !=
          unescape(location.search)) {
       top.location.hash =
          unescape(location.search.substring(1));
       top.laden();
    }
    //--></script>
Hier noch einmal zur Ergänzung das komplette Markup samt JavaScript-Code für die HTML-Seite:
    <html>
    <head>
    <title>AJAX</title>
    <script type="text/javascript"><!--
    var http = null;
    var zustand = location.hash;

    var laden = function() {
       if (location.hash.indexOf("geladen") > –1) {
          ladeJSON();
       } else {
          document.getElementById("Liste").innerHTML = "";
       }
       zustand = location.hash;
    }
    window.onload = laden;
    window.setInterval(function() {
       if (zustand != location.hash) {
          laden();
       }
       with (window.frames["historyframe"].window.location) {
          if (search != "?" + escape(zustand.substring(1)) &&
              window.ActiveXObject) {
             search = "?" + escape(zustand.substring(1));
          }
       }
    }, 500);

    function ladeJSON() {
       if (window.XMLHttpRequest) {
          http = new XMLHttpRequest();
       } else if (window.ActiveXObject) {
          http = new ActiveXObject("Microsoft.XMLHTTP");
       }
       if (http != null) {
          http.open("GET", "json.txt", true);
          http.onreadystatechange = ausgeben;
          http.send(null);
       }
    }

    function ausgeben() {
       if (http.readyState == 4) {
          var daten = http.responseText;
          daten = eval("(" + daten + ")");
          var liste = document.getElementById("Liste");
          for (var i = 0; i < daten.length; i++) {
             var link = daten[i];
             var li = document.createElement("li");
             var a = document.createElement("a");
             a.setAttribute("href", link.url);
             var txt = document.createTextNode(link.text);
             a.appendChild(txt);
             li.appendChild(a);
             liste.appendChild(li);
          }

          location.hash = "geladen";
          zustand = location.hash;
       }
    }
    //--></script>
    </head>
    <body>
    <ul id="Liste"></ul>
    <form>
       <input type="button" value="Laden"
          onclick="ladeJSON();" />
    </form>
    <iframe
       src="history.html"
       name="historyframe"
       style="display:none"></iframe>
    </body>
    </html>
Das Ergebnis der ganzen Mühe: Das Beispiel funktioniert immer noch in Mozilla-Browsern; der Internet Explorer spielt jetzt aber auch mit. Jedes Neuladen des Iframe erzeugt einen Eintrag in der Verlaufsliste des Browsers, wie Sie in Abbildung 18.12 sehen können. Durch unseren zusätzlichen Code führt ein Sprung zurück oder nach vorne dazu, dass der Seiteninhalt aktualisiert wird. Sie sehen: Die größten AJAX-Nachteile, die Bookmarkfähigkeit und das Vor-/Zurückspringen, lassen sich mit etwas Code lösen. So weit die gute Nachricht. Die schlechte Nachricht ist: Die Programmierung kann recht aufwändig werden, gerade beim letzteren Punkt. Die genauen Details der Implementierung hängen natürlich auch sehr stark von dem Anwendungsszenario ab. Bei nur zwei möglichen Zuständen wie im Beispiel ist das natürlich einfacher, als wenn Sie auf einer Seite eine Handvoll dynamische DOM-Elemente verwenden. Hier klicken, um das Bild zu Vergrößern Abbildung 18.12 Mit einem Trick funktionieren auch beim Internet Explorer »Vor« und »Zurück« wieder. 1 Werfen Sie mal einen Blick in dickere, spezifische AJAX-Bücher: Dort finden Sie häufig auch viele JavaScript-Grundlagen, denn mit AJAX allein lassen sich gar nicht so viele Seiten füllen.
<< zurück | <top> | vor >>
Zum Katalog
JavaScript und AJAX bestellen
Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen? Ihre Meinung
Buchtipps
| JavaScript und AJAX - Das Video-Training
| Webseiten programmieren und gestalten
| XHTML, HTML und CSS
| CSS-Praxis
| AJAX
| PHP 5 und MySQL 5
| TYPO3 4.0
Shopping
Versandkostenfrei bestellen in Deutschland und Österreich Info

Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

[Galileo Computing]
Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de