Aus Linux-Magazin 02/2007

Bookmarks und der Zurück-Button in Ajax-Anwendungen

© photocase.com

Die größte Stärke der Ajax-Technologie ist, dass sie das Aktualisieren einer Seite ohne störendes Neuladen erlaubt. Als Kehrseite folgt daraus allerdings, dass sich der Zurück-Button des Browsers und die Bookmark-Funktion nicht mehr wie gewohnt nutzen lassen.

Vorwärts- und Zurück-Buttons, eine Browse-History und Bookmarks sind wichtige Hilfsmittel beim Surfen im Internet, die jeder Browser seinem Benutzer anbietet. Doch bei Websites, die die Ajax-Technologie einsetzen, gibt es gerade mit diesen Funktionen ein Problem: Da Ajax-Seiten auf Reloads verzichten und stattdessen immer nur die bereits geöffnete Seite mit Hilfe von Javavscript dynamisch aktualisieren, bleibt aus Sicht des Browsers während der gesamten Session die Startseite geladen. Er legt daher keine Einträge in der Browse-History an und der Zurück-Button lässt sich nicht benutzen.

Ein Vergleich verdeutlicht den Unterschied zu konventionellen Webanwendungen: Ein Suchformular, das für die Übermittlung der Suchbegriffe zum Server die Get-Methode benutzt wie das Suchformular der Google-Websuche, erlaubt es, Suchergebnisse für bestimmte Begriffe zu speichern: Die eingegebenen Begriffe sind in der URL in den Parametern hinter dem Fragezeichen enthalten. Bei der Ajax-Anwendung Google Maps hingegen zeigt die Adressenleiste des Browsers – und damit jede angelegte Bookmark – stets »maps.google.de«, egal welchen Kartenausschnitt der Anwender wählt oder welchen Ort er auf der Karte aktiviert.

Als einen Workaround bietet Google Maps rechts oben im Fenster eine URL mit Get-Parametern, deren Aufruf den aktuellen Zustand der Kartenansicht wiederherstellt (Abbildung 1).

Abbildung 1: Notlösung: In Google Maps findet sich rechts oben auf der Seite ein Link, dessen Aufruf den gewählten Kartenausschnitt wiederherstellt. Eine Bookmark reicht hierfür nicht aus.

Abbildung 1: Notlösung: In Google Maps findet sich rechts oben auf der Seite ein Link, dessen Aufruf den gewählten Kartenausschnitt wiederherstellt. Eine Bookmark reicht hierfür nicht aus.

In dubio pro

Zwar lässt sich darüber streiten, ob es bei Ajax-Anwendungen, die sich im Look&Feel Desktop-Anwendungen annähern, nötig ist, den gesamten Verlauf der Arbeit über die Vorwärts- und Zurück-Buttons des Browsers abrufen zu können oder als Bookmarks abzulegen. Bei Suchformularen oder Anwendungen, deren Zustand sich nicht durch das Abspeichern und Öffnen von Dokumenten wiederherstellen lässt, sollte dies aber auf jeden Fall möglich sein, sonst besteht die Gefahr, dass die Anwender die Ajax-Technologie eher als Einschränkung empfinden, nicht als Erleichterung.

Ausgetrickst

Eine Möglichkeit, den Status einer Ajax-Anwendung mit der URL zu verknüpfen, ist der Einsatz des HTML-Ankers: Das »#«-Zeichen in URLs dient normalerweise dazu, bestimmte Stellen in einer HTML-Seite zu referenzieren. Ein Klick auf den Link »http://xxyz.de/Ajax.html#Kapitel_5« veranlasst den Browser zu der Stelle zu springen, an der das Tag »<a name=”Kapitel_5″></a>« steht. Existiert kein Anker mit diesem Namen, bleibt die Anzeige unverändert.

Dieses Verhalten prädestiniert HTML-Anker dazu, den Status von Ajax-Anwendungen zu speichern: Sie bieten eine Möglichkeit, die URL zu verändern, ohne dass der Browser von sich aus darauf reagiert. Das Update der Anzeige erledigt die Ajax-Anwendung wie üblich mit Hilfe von Javascript. Über den Teil der URL hinter dem »#«-Zeichen kann sie, ebenfalls per Javascript, frei verfügen, um so Statusinformationen zu speichern (Abbildung 2).

Abbildung 2: Der HTML-Anker nach dem #-Zeichen in URLs lässt sich als Status-ID missbrauchen, sodass sich deren Zustand in einer Bookmark erfassen lässt. Für Bookmarks sind noch einige Javascript-Tricks erforderlich.

Abbildung 2: Der HTML-Anker nach dem #-Zeichen in URLs lässt sich als Status-ID missbrauchen, sodass sich deren Zustand in einer Bookmark erfassen lässt. Für Bookmarks sind noch einige Javascript-Tricks erforderlich.

Abbildung 3: Der versteckte Iframe sorgt auch im Internet Explorer dafür, das jede Statusänderung einen History-Eintrag hinterlässt. Für Bookmarks kommen wie bei anderen Browsern HTML-Anker zum Einsatz.

Abbildung 3: Der versteckte Iframe sorgt auch im Internet Explorer dafür, das jede Statusänderung einen History-Eintrag hinterlässt. Für Bookmarks kommen wie bei anderen Browsern HTML-Anker zum Einsatz.

Hierbei gibt es allerdings eine Einschränkung: Browser, Webserver und Proxyserver beschränken URLs auf unterschiedliche maximale Gesamtlängen. Der Hashwert eignet sich also nicht zum Speichern umfangreicher Informationen. Soll der Hash dazu dienen, umfangreiche Session-Informationen zu speichern, kann der Anker dabei nur als eindeutiger Identifikationsstring dienen. Die Informationen selbst muss die Anwendung in einer Datenbank ablegen.

Der normale Query-String nach einem Fragezeichen (»?Status1«) eignet sich nicht für diesen Zweck, da seine Veränderung ein Neuladen der Seite und damit das unschöne Flackern hervorrufen würde, das die Programmiertechnik Ajax eigentlich vermeiden soll.

In die Praxis

Um den Anker in der URL zu verändern, stellt das Javascript-Objekt »location« die Eigenschaft »hash« zur Verfügung: »location.hash = “#” . escape(“Hashwert”);« setzt den Wert, die Befehle

if (location.hash.length > 1) {
  var hashvalue = unescape(location.hash.substring(1));
}

lesen ihn aus. Dass »location.hash« nur Zugriff auf den Anker, nicht jedoch auf die restliche URL bietet, hilft Cross-Site-Scripting-Attacken zu verhindern. Da der Benutzer den Hashwert manipulieren kann, ist bei der Weiterverarbeitung jedoch die für Webanwendungen obligatorische Sorgfalt erforderlich.

Probleme bereiten die Browser Safari und Konqueror: Zum einen gibt hier »location.hash« den Anker unter Umständen mit einem Doppelkreuz zurück. Zum anderen legen beide Browser beim Ändern des Hashwerts nicht immer einen neuen Eintrag in der History-Liste an. Einen Überblick über die Problematik liefert [1]. Der einzige Ausweg ist, über eine Browserweiche bei Safari und Konqueror Query-Strings (»URL?Status1«) einzusetzen und auf den Vorteil, Seiten dynamisch ohne Reload verändern zu können, zu verzichten.

Geschichtsrelevant

Um bestimmte Zustände von Ajax-Anwendungen in Bookmarks abzulegen, reicht es, wenn die Ajax-Anwendung die Statusinformationen in URLs der Form »http://xyz.de/Ajax_Probleme.html# Status1« speichert. Um das Zurück-und-Vorwärts-Button-Problem zu lösen (Abbildung 2), ist noch etwas mehr Aufwand erforderlich: Die Browser passen nach dem Verändern der URL über »location.hash« zwar sofort den Wert in der Adressenleiste an, der Zurück-Button funktioniert aber erst, wenn die Anwendung über einen »XMLHttpRequest« neue Daten vom Server geladen hat: Erst dann geht der Browser davon aus, dass sich die Anzeige verändert hat und legt einen History-Eintrag an.

Die Javascript-Funktion »lade_info()« in Listing 1 sorgt dafür, dass dies geschieht: Zuerst prüft sie, ob das »XMLHttpRequest«-Objekt korrekt angelegt wurde (dieser Code fehlt im Beispiel). Dann prüfen die Zeilen 8 und 11 den Hash auf die zulässigen Werte »Datei1« oder »Datei2«. Der Event-Handler »onreadystatechange« stellt sicher, dass die Daten geladen wurden, und gibt sie anschließend zur Kontrolle in das »<div>«-Element mit der ID »ausgabe« aus. Für das vorliegende Beispiel ist es unwichtig, ob es sich bei »Datei1« und »Datei2« um reine Textdateien handelt oder um Skripte, die Daten im XML-Format liefern. Wichtig ist nur, dass der Inhalt nicht gleich ist.

Listing 1: History-Einträge
erzeugen

01 function lade_info() {
02   if (xhr != null) {
03         if (location.hash.length > 1) {
04           schritt = unescape(location.hash.substring(1));
05         } else {
06           return false;
07         }
08     if (schritt == "skript1") {
09       xhr.open("GET", "Datei1");
10         } else if (schritt == "skript2") {
11       xhr.open("GET", "Datei2");
12         } else {
13           return false;
14         }
15     xhr.onreadystatechange = function() {
16       if (xhr.readyState == 4) {
17       var ausgabe = document.getElementById("ausgabe");
18       ausgabe.innerHTML = xhr.responseText;
19     }
20         }
21     xhr.send(null);
22   }
23 }

Statuskontrolle

Im nächsten Schritt sorgen Event-Handler dafür, dass die Funktion »lade_info()« beim Laden und danach alle 200 Millisekunden aufgerufen wird:

window.onload = function() {
  lade_info();
}
window.setInterval("lade_info()", 200);

Dies stellt sicher, dass der Browser nach jeder Statusänderung schnell Kontakt mit dem Server hat und einen neuen History-Eintrag erzeugt.

Für den Statuswechsel sorgen in der einfachen Beispielanwendung zwei Schaltflächen im HTML-Code, die jeweils eine Javascript-Funktion aufrufen, die den Anker hinter dem »#«-Zeichen in der URL verändert:

<div>
  <input type="button" onclick="setze_schritt('skript1')" value="Schritt 1" />
  <br />
  <input type="button" onclick=  <"setze_schritt('skript2')" value="Schritt 2" />
</div>

Die aufgerufe Funktion, die URLs nach Statusänderungen anpasst, lautet:

function setze_schritt(schritt) {
  location.hash = "#" + escape(schritt);
  lade_info();
}

Diese Kombination aus der Anpassung der URL und dem Neuladen von Daten vom Server gewährleistet, dass sowohl der Zurück-Button als auch das Ablegen des Seitenstatus in einer Bookmark funktionieren.

Noch ein Workaround

Beim Internet Explorer führt auch das beschriebene Verfahren noch nicht zum Erfolg, da dieser Browser in seiner History-Liste zwar Get-Parameter wie »?Status1«, jedoch keine HTML-Anker berücksichtigt. Hier hilft nur ein weiterer Workaround, ein versteckter Iframe:

»<iframe name="versteckt" href="iframe.html" style="display:none" />«. 

Dem im Iframe geladenen Javascript (Datei »iframe.html«) übermittelt die Ajax-Anwendung den Status-String als Get-Parameter und sorgt dadurch für einen Eintrag in der History-Liste. Eine Erweiterung der »setze_schritt()«-Funktion aus dem vorigen Beispiel erledigt diese Aufgabe, wenn das »ActiveXObject« existiert und folglich der Internet Explorer das Skript ausführt:

function setze_schritt(schritt) {
  location.hash = "#" + escape(schritt);
  if (window.ActiveXObject) {
    window.frames["versteckt"].window.location.search = "?" + escape(schritt);
  }
  lade_info();
}

Der Javascript-Code im Iframe wertet den übermittelten Get-Parameter aus und ersetzt den Hash in der URL der Hauptseite durch den übermittelten Parameter, wenn dieser vom bereits dort stehenden Wert abweicht:

window.onload = function() {
  if (location.search.length > 1 &&
      top.location.hash.substring(1) !=unescape(location.search.substring(1))) {
    top.lade_info();
  }
}

Erst diese Kettenreaktion stellt im Internet Explorer die gewohnte Browserfunktionalität sicher: Das Anhängen eines Get-Parameters »?Status1« an die URL der im Iframe versteckten Unterseite sorgt für einen Eintrag in der History-Liste. Dass Microsofts Browser dabei die Seite im Iframe neu lädt, spielt keine Rolle, da sie nicht sichtbar ist. In der URL der übergeordneten Seite taucht der Parameter noch ein zweites Mal auf, diesmal als Hash »#Status1«. Damit sorgt er dafür, dass sich der Zustand der Ajax-Anwendung mit einer Bookmark archivieren lässt.

Fazit

Dieser Artikel hat Verfahren vorgestellt, die bewirken, dass der Zurück-Button und die Bookmark-Funktion auch bei Ajax-Anwendungen wie erwartet funktionieren. In der Praxis sorgen hierfür Ajax-Bibliotheken, ohne dass der Entwickler den Code von Hand schreiben muss. Allerdings gibt es bei den Implementierungen Qualitätsunterschiede, sodass es nicht schaden kann, die eingesetzte Technik zu prüfen. Jedenfalls sollte keine Ajax-Website wie die Windows-Live-Suche ohne Zurück-Button und Bookmark-Unterstützung online gehen: Die Reaktion der Presse zeigt, was für ein schlechter Eindruck sich aus solchen Einschränkungen ergibt. (pkr)

Infos

[1] Ajax-Probleme in Konqueror und Safari: [http://codinginparadise.org/weblog/2005/09/safari-no-dhtml-history-possible.html]

Der Autor

Tobias Hauser ist Autor, Trainer und Berater mit Schwerpunkt Webentwicklung. Er schreibt regelmäßig für Fachzeitschriften und spricht auf Konferenzen im In- und Ausland. Als Teilhaber der Agentur Arrabiata Solutions GmbH realisiert er Web- und Intranetprojekte, führt Usability-Audits durch und beschäftigt sich mit Content Management für das Web 2.0.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben