![]() |
Javascript-Wertübergabe zwischen verschiedenen HTML-Dokumenten |
|
| |
| E-Mail: |
|---|
Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!
Der folgende Artikel ist lediglich eine Zusammenführung und Überarbeitung zweier älterer Artikel, die ihrerseits
Hatto von Hatzfeld und Stefan Puff verfasst haben. Dank geht auch an Torsten Anacker, dessen Code zur
Zerlegung des Querystrings Pate gestanden hat, sowie Thomas Fischer, der einst Anregungen zur Verwendung von window.name gab.
Oft möchten Javascript-Programmierer Variablen, die sie in einem Dokument definiert haben, auch in einem anderen Dokument, das anschließend im selben Browserfenster geladen wird, verfügbar haben. Üblicherweise werden für diesen Zweck
Cookies verwendet. Doch nicht immer haben Nutzer Cookies zugelassen oder wollen sie nur nach Einzelbestätigung akzeptieren.
Eine anderer Ansatz ist die Verwendung von
Frames, da dort bei wechselnden Frameinhalten das Frameset selbst einschließlich der zugehörigen Javascript-Variablen erhalten bleibt, bis das Frameset z.B. über einen speziellen Verweis verlassen wird (
Verweise zum Beenden von Framesets).
Mittlerweile ist die Verwendung von Frames nur sehr selten angemessen und gewünscht – die meisten Webseiten verzichten absichtlich auf Frames. Wenn die Webseite bereits
Frames einsetzt, bietet es sich jedoch an, diese vorhandene Struktur zu nutzen.
Ein Script in einem Frame-Dokument hat die Möglichkeit, auf Objekte und Variablen des übergeordneten Frameset-Dokuments zuzugreifen (und umgekehrt). Jedes Dokument des Framesets stellt ein Fenster mit einem eigenen
Fensterobjekt dar. Eine Schlüsselrolle beim frame-übergreifende Arbeiten spielt das
frames-Objekt. Diese Merkmale kann man sich zum Zweck der Wertübergabe zunutze machen, indem ein Frame-Dokument Daten im Objektbereich des Framesets-Dokuments hinterlegt und ein anderes Frame-Dokument diese Daten ausliest. Der frame-übergreifende JavaScript-Zugriff wird im Artikel
Zugriff auf verschachtelte Fensterobjekte näher beschrieben.
Im Folgenden wird eine Beispielanwendung beschrieben, bei welcher die in ein Formular eingegebenen Daten im Frameset-Dokument zwischengespeichert werden und von einem weiteren Frame-Dokument zurückgelesen und ausgegeben werden.
Anzeigebeispiel: So sieht's aus
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Wertübergabe mittels Framestruktur</title>
</head>
<frameset rows="20%,80%">
<frame name="frame1" src="frame_egal.htm">
<frame name="frame2" src="frame_sender.htm">
</frameset>
</html>
Es wird ein einfaches Frameset mit zwei Frames definiert, in welchem das Dokument geladen wird, das die Variablen setzt, die übergeben werden sollen. Der Dateiname egal.html ist als Platzhalter zu verstehen, der für jedes beliebige HTML-Dokument stehen kann.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Wertübergabe mittels Framestruktur - Sendendes Dokument</title>
<script type="text/javascript">
function uebergabe () {
parent.vorname = document.forms.formular.elements.vorname.value;
parent.nachname = document.forms.formular.elements.nachname.value;
location.href = "frame_empfaenger.htm";
return false;
}
</script>
</head>
<body>
<form name="formular" action="" onsubmit="return uebergabe();">
<p>Wie heißen Sie?</p>
<p>
<label for="vorname">Vorname:</label>
<input type="text" name="vorname" id="vorname" size="25">
</p>
<p>
<label for="nachname">Nachname:</label>
<input type="text" name="nachname" id="nachname" size="25">
</p>
<p><input type="submit" value="Senden"></p>
</form>
</body>
</html>
Das Dokument frame_sender.htm stellt das gebende bzw. sendende Dokument dar. Das Formular dient lediglich als Eingabemaske. Nachdem der Benutzer das Formular ausgefüllt hat und das Formular absendet, wird über das submit-Ereignis die Funktion uebergabe() aufgerufen. Sie liest die Werte der Eingabefelder Vorname und Nachname aus (siehe
Zugriff auf Elemente eines Formulars) und speichert sie als Variablen vorname und nachname im übergeordneten Frameset-Dokument. Dieses wird über den reservierten Fensternamen parent (Eltern-Fenster) angesprochen. Nach der Übergabe leitet die Funktion mittels
location.href zum Dokument frame_empfaenger.htm weiter.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>Wertübergabe mittels Framestruktur - Empfangendes Dokument</title> </head> <body> <p> Sie heißen: <script type="text/javascript"> document.write(parent.vorname + ' ' + parent.nachname); </script> </p> </body> </html>
Die zweite, empfangende Seite liest die Werte aus den Variablen im Frameset-Dokument wieder über parent.vorname bzw. parent.nachname aus und schreibt sie mittels
document.write in das Dokument. Selbstverständlich ist es auch möglich, diese Werte wie gewohnt auf andere Weise zu verwenden bzw. zu verarbeiten.
Angenommen, man gibt in das Formular auf frame_sender.htm den Vornamen "George" und den Nachnamen "Michael" ein, so sollte frame_empfaenger.htm den Text "Sie heißen: George Michael" enthalten.
Dieser Lösungsansatz hat gegenüber den noch folgenden den großen Vorteil, dass die Daten zur Übergabe nicht kodiert und wieder dekodiert werden müssen, sondern direkt als gewöhnliche JavaScript-Variablen zur Verfügung stehen. Dies bedeutet auch, dass problemlos komplexere Daten wie etwa Arrays oder eigene Objekte übergeben werden können. Der Nachteil ist, dass er eine Framestruktur voraussetzt, da es nicht sinnvoll ist, Frames alleinig zum Zweck der JavaScript-Wertübergabe zu verwenden. Die Beispiele gehen ferner stillschweigend davon aus, dass JavaScript aktiviert ist.
Ein weiterer Lösungsansatz nutzt die Adresse des empfangenden Dokuments, um die gewünschten Daten als Abfrageparameter zu übergeben. Dabei wird der URL ein sogenannter Query String angehängt, welcher nach einem Fragezeichen die Daten in einer kodierten Form enthält. Ein Query String mit mehreren Parameter/Wert-Paaren ist nach dem Schema ?name1=wert1&name2=wert2&name3=wert3&... aufgebaut. Dieser Mechanismus wird unter anderem bei der Übergabe von Formulardaten über die HTTP-Methode GET genutzt. Im empfangenden Dokument können die URL-Parameter über die JavaScript-Objekteigenschaft
location.search ausgelesen, getrennt und dekodiert werden.
Ein großer Nachteil dieser Variante ist allerdings, dass das empfangende Dokument neu vom Server angefordert werden muss, selbst wenn es bereits im Cache (Zwischenspeicher) des Browsers oder eines Proxy-Servers liegen. Dies führt auch dazu, dass der Offline-Modus der meisten Browser im Zusammenhang mit der Wertübergabe über die URL nicht funktioniert.
Um die Daten über die URL übergeben zu können, müssen sie vorher URL-kodiert werden, das heißt für die Übertragung vorbereit werden. Dabei lässt sich die vordefinierte Methode
escape() verwenden, mit welcher Sonder- und Steuerzeichen in Parameternamen und Wertstrings maskiert werden können.
Während es bei der zuvor beschriebenen Framelösung möglich ist, alle möglichen Objekte bzw. Variablen gleich welchen Typs auf einfache Weise weiterzugeben, sind bei der Übergabe mittels URL letztlich nur
String-Variablen möglich. Bei Variablen des Types
Boolean und
Number ist dies unproblematisch, da JavaScript sie automatisch in String-Variablen umwandeln kann, ohne dass Informationen verloren gehen. Bei komplexeren Datenstrukturen wie
Arrays oder
eigenen Objekten hingegen sind aufwendigere Techniken notwendig, um die Array-Elemente bzw. Objekteigenschaften jeweils in Parameter/Wert-Paare umzuwandeln. Im Folgenden wird ein Weg zur Übergabe von einfachen Variablen beschrieben.
Anzeigebeispiel: So sieht's aus
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Wertübergabe mittels URL - Sendendes Dokument</title>
</head>
<body>
<form method="GET" action="querystring-empfangen.htm">
<p>Wie heißen Sie?</p>
<p>
<label for="vorname">Vorname:</label>
<input type="text" name="vorname" id="vorname" size="25">
</p>
<p>
<label for="nachname">Nachname:</label>
<input type="text" name="nachname" id="nachname" size="25">
</p>
<p><input type="submit" value="Senden"></p>
</form>
</body>
</html>
Das Eingabeformular kennen wir bereits. Geändert hat sich das action-Attribut, in dem die URL des empfangenden Dokuments angegeben wird. Auch wenn GET die Standard-Methode zum Übertragen von Formulardaten ist, geben wir zur Verdeutlichung zusätzlich method="GET" an.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Wertübergabe mittels URL - Empfangendes Dokument</title>
<script type="text/javascript">
function Werteliste (querystring) {
if (querystring == '') return;
var wertestring = querystring.slice(1);
var paare = wertestring.split("&");
var paar, name, wert;
for (var i = 0; i < paare.length; i++) {
paar = paare[i].split("=");
name = paar[0];
wert = paar[1];
name = unescape(name).replace("+", " ");
wert = unescape(wert).replace("+", " ");
this[name] = wert;
}
}
var liste = new Werteliste(location.search);
</script>
</head>
<body>
<h1>Übergebene Daten</h1>
<table border="1" cellpadding="5" cellspacing="0">
<tr>
<th>Feldname</th>
<td>Eintrag</td>
</tr>
<script type="text/javascript">
for (var eigenschaft in liste) {
document.write(
"<tr><td>" + eigenschaft + "</td>" +
"<td><code>" + liste[eigenschaft] + "</code></td></tr>"
);
}
</script>
</table>
</body>
</html>
Zuerst werden die Formulardaten mittels location.search aus der URL extrahiert und der Funktion Werteliste() übergeben. Dessen Aufruf mit dem new-Operator erzeugt ein
eigenes Objekt. Werteliste tritt als Konstruktor-Funktion auf und gibt die erzeugte Objekt-Instanz zurück, die in der Variable liste gespeichert wird.
In der Funktion Werteliste() werden zuerst die Datenpaare, bestehend aus Parametername und Wert, und anschließend jedes Wertepaar getrennt. Die Trennung der Datenpaare erfolgt mithilfe der
split-Methode. Sie gibt einen Array zurück, der anschließend mit einer for-Schleife durchlaufen wird. Jedes Element dieses Array steht für ein Datenpaar-String nach dem Schema name=wert. Mit erneuter Anwendung von split() wird der String in Name und Wert getrennt, die in den Variablen name bzw. wert zwischengespeichert werden.
Danach folgt die Dekodierung mit der Methode
unescape(). Zusätzlich wird das Zeichen + mittels
replace() durch ein Leerzeichen ersetzt, denn unescape() lässt diese notwendige Dekodierung aus.
Die ausgelesenen und dekodierten Werte werden schließlich mit this[name] = wert als Eigenschaften am neu erzeugten Objekt gespeichert.
Das Ergebnis der Dekodierung steht in der Variable liste in Form eines Objekts mit mehreren Eigenschaften zur Verfügung. (Sollten keine Werte übergeben werden, z.B. wenn die Seite direkt aufgerufen wird, hat das Objekt einfach keine Eigenschaften.) Das liste-Objekt verhält sich wie ein
assoziativer Array: Wenn einen Datenpaar mit dem Namen »vorname« übergeben wurde, können wir dessen Wert mit der Schreibweise liste.vorname auslesen. Wenn der Name Leerzeichen oder Sonderzeichen enthält, z.B. »Name des Haustiers«, müssen wir uns der Alternativ-Schreibweise zum Zugriff auf Objekteigenschaften bedienen: liste["Name des Haustiers"].
Im Dokumentkörper des Beispiels wird mittels JavaScript eine Tabelle generiert, in der die Daten aus dem liste-Objekt ausgegeben werden. Ein solches Objekt muss mit einer
for-in-Schleife durchlaufen werden.
window.nameDas window-Objekt kennt die Eigenschaft
name. Sie bleibt über die ganze »Lebensdauer« des Browserfensters erhalten und kann durch eine Javascript-Zuweisung geändert werden. So bietet es sich an, diese Eigenschaft zur Sicherung von Daten zu nutzen, die einen Wechsel des Fensterinhaltes überstehen sollen.
Anzeigebeispiel: So sieht's aus
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Wertübergabe mittels window.name - Sendendes Dokument</title>
</head>
<body>
<script type="text/javascript" src="storage.js"></script>
<script type="text/javascript">
function sichern () {
storage.set("vorname", document.forms.formular.elements.vorname.value);
storage.set("nachname", document.forms.formular.elements.nachname.value);
location.href = "window-name-empfaenger.html";
}
</script>
<form name="formular" action="" onsubmit="sichern(); return false;">
<p>Wie heißen Sie?</p>
<p>
<label for="vorname">Vorname:</label>
<input type="text" name="vorname" id="vorname" size="25">
</p>
<p>
<label for="nachname">Nachname:</label>
<input type="text" name="nachname" id="nachname" size="25">
</p>
<p><input type="submit" value="Senden"></p>
</form>
</body>
</html>
var storage = new function () {
/* --------- Private Properties --------- */
var dataContainer = {};
/* --------- Private Methods --------- */
function linearize () {
var string = "", name, value;
for (name in dataContainer) {
name = encodeURIComponent(name);
value = encodeURIComponent(dataContainer[name]);
string += name + "=" + value + "&";
}
if (string != "") {
string = string.substring(0, string.length - 1);
}
return string;
}
function read () {
if (window.name == '' || window.name.indexOf("=") == -1) {
return;
}
var pairs = window.name.split("&");
var pair, name, value;
for (var i = 0; i < pairs.length; i++) {
if (pairs[i] == "") {
continue;
}
pair = pairs[i].split("=");
name = decodeURIComponent(pair[0]);
value = decodeURIComponent(pair[1]);
dataContainer[name] = value;
}
}
function write () {
window.name = linearize();
}
/* --------- Public Methods --------- */
this.set = function (name, value) {
dataContainer[name] = value;
write();
};
this.get = function (name) {
var returnValue = dataContainer[name];
return returnValue;
};
this.getAll = function () {
return dataContainer;
};
this.remove = function (name) {
if (typeof(dataContainer[name]) != undefined) {
delete dataContainer[name];
}
write();
};
this.removeAll = function () {
dataContainer = {};
write();
};
/* --------- Construction --------- */
read();
};
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Wertübergabe mittels window.name - Empfangendes Dokument</title>
<script type="text/javascript" src="storage.js"></script>
<script type="text/javascript">
var liste = storage.getAll();
</script>
</head>
<body>
<h1>Übergebene Daten</h1>
<table border="1" cellpadding="5" cellspacing="0">
<tr>
<th>Feldname</th>
<th>Eintrag</th>
</tr>
<script type="text/javascript">
for (var eigenschaft in liste) {
document.write(
"<tr><td>" + eigenschaft + "</td>" +
"<td><code>" + liste[eigenschaft] + "</code></td></tr>"
);
}
</script>
</table>
</body>
</html>
Um das Schreiben und Auslesen von Daten über window.name zu erleichern, wird ein eigenes Objekt namens storage eingeführt. Dessen Definition finden wir in in der externen JavaScript-Datei storage.js, die im sendenden und im empfangenden Dokument eingebunden wird (siehe
JavaScript in separaten Dateien). Die Nutzung dieses Fertigscriptes ist denkbar einfach – zum Verständnis der internen Umsetzung ist jedoch grundlegendes Wissen über objektorientierte Programmierung notwendig. Wenn Sie die folgenden Erklärungen nicht auf Anhieb verstehen, so ist das nicht schlimm.
Das eigene Objekt storage wird new function () { ... } definiert. Das bedeutet, es wird eine Instanz der folgenden Funktion erzeugt. Lassen Sie sich nicht verwirren, dies ist nichts anderes als das Instantiieren von Objekten nach dem bekannten Schema var instanz = new Konstruktor(). Das Besondere hier ist lediglich, dass die Konstruktor-Funktion anonym notiert wurde, also keinen Namen hat. Wir könnten ebenso schreiben:
function storageConstructor () {
/* ...*/
}
var storage = new storageConstructor();
Allerdings wird durch die Kurzschreibweise mit der anonymen Funktion die überflüssige globale Variable storageConstructor vermieden.
In dieser anonymen Konstruktor-Funktion befindet sich jedenfalls die gesamte Logik:
dataContainer. Dies Object in welchem alle Namen/Werte-Paare gespeichert werden.linearize() erzeugt aus den Daten im dataContainer einen String, der dann in window.name gespeichert werden kann. Als Kodierungsformat wird die bekannte URL-Kodierung verwendet (name1=wert1&name2=wert2&...).read() liest window.name aus, dekodiert die darin gespeicherten Daten und legt sie als JavaScript-Objekte im dataContainer ab.write() ruft die Linearisierungsfunktion auf und schreibt deren Rückgabewert in window.name.storage.methodenName() aufgerufen werden können:
set(name, value) speichert einen Wert unter einem bestimmten Namen. Erwartet dazu den Namen und den Wert als Parameter.get(name) liefert den gespeicherten Wert mit dem angegebenen Namen zurück. Erwartet dazu den Namen als Parameter.getAll() liefert alle gespeicherten Werte als Object zurück. Erwartet keine Parameter.remove(name) entfernt den gespeicherten Wert mit dem angegebenen Namen. Erwartet dazu den Namen als Parameter.removeAll() entfernt alle gespeicherten Werte. Erwartet keine Parameter.read() aufgerufen, um beim Laden des Dokuments die in window.name gespeicherten Daten auszulesen und sie später bereitstellen zu können.Im sendenden Dokument befindet sich das bekannte Namens-Formular. Beim Absenden (onbsubmit) wird eine Funktion aufgerufen, die die Formulardaten mittels storage in window.name speichert. Dazu wird einfach die set-Methode aufgerufen und ihr wird der Wert des Formularfeldes übergeben:
storage.set("vorname", document.forms.formular.elements.vorname.value);
storage.set("nachname", document.forms.formular.elements.nachname.value);
Danach wird mit location.href zur empfangenden Seite weitergeleitet. Diese liest mittels storage.getAll() alle gespeicherten Werte aus. Im Dokumentkörper wird das zurückgelieferte Objekt durchlaufen und deren Name/Wert-Paare ausgegeben – wir kennen auch dies bereits aus dem vorigen Lösungsansatz. Möchten wir nur einen einzigen gespeicherten Wert auslesen, so nutzen wir get():
var vorname = storage.get("vorname");
Mit dem vorgestellten storage-Objekt lassen sich komfortabel einfache Daten zwischen verschiedenen, nacheinander geladenen Dokumenten austauschen. Eine rigide Größenbeschränkung von window.name scheint nicht zu existieren, selbst eine Zeichenkette von mehreren 100 Kilobyte wird erfolgreich verarbeitet. Es dürfte jedoch geraten sein, in der regelmäßigen Verwendung nicht über einige Kilobyte hinauszugehen. Sie sollten ebenfalls beachten, dass der Zugriff auf das Fenster über dessen Namen durch den »Missbrauch« von window.name erschwert, wenn nicht unmöglich werden.
Mittlerweile gibt es
Helferscripte, die das Lesen und Setzen von Cookies über document.cookie vereinfachen. Trotzdem bleibt immer das Risiko, dass der Browser bzw. der Anwender Cookies ablehnt oder sie durch einen Proxy schon im vorhinein ausgefiltert werden. Der Lösungsansatz mit window.name kann in dem Fall eine Alternative sein.
Ob document.cookie oder window.name: Clientseitige Lösungen haben ähnliche Nachteile und sind nur in bestimmten Fällen geeignet. Die meisten dynamischen Webanwendungen setzen auf sogenannte Sessions (Sitzungen), eine serverseitige Lösung zum vorübergehenden Speichern von Daten. Clientseitig wird dann nur die Session-ID, die eindeutige Kennung der Sitzung gespeichert –- üblicherweise in einem Cookie, der aber nicht mit JavaScript, sondern ganz einfach HTTP gesetzt wird. Eine Beispielumsetzung in PHP stellt der Artikel
Sessionbasiertes Loginsystem vor. Für serverseitig dynamische Webseiten wie etwa Foren, Communities, Online-Shops ist diese Lösung ist zuverlässiger und sicherer als eine eventuelle JavaScript-Lösung.
JavaScript-Wertübergabe ist also kein Ersatz für Sessions, sondern deckt andere Problemstellungen ab. Wenn Sie sowieso JavaScript voraussetzen können oder optionale Zusatz-Features mit JavaScript entwickeln, dann ist window.name ideal, um kleinere Einstellungen zu speichern. Ein Beispiel ist ein Script, dass die individuelle Anpassung der Schriftgröße erlaubt.
Wichtiger Sicherheitshinweis: Cookies können nur von der Webseite ausgelesen werden können, die sie auch gesetzt hat. Dieses grundlegende und entscheidend wichtige Sicherheitskonzept nennt sich Same Origin Policy. Demgegenüber müssen Sie sich unbedingt im Klaren darüber sein, dass window.name auch für fremde Webseite lesbar ist, die später im selben Fenster angezeigt werden. Für window.name gilt keine Same Origin Policy! Deshalb sollten Sie darin niemals vertrauliche Daten speichern, ansonsten ist im schlimmsten Fall
Session Hijacking und
Identitätsdiebstahl Tür und Tor geöffnet.
Neben document.cookie und window.name entsteht momentan eine weitere Technik, die diesmal komfortable Methoden zum Speichern und Lesen von Werten bereits mitbringen soll. Die Rede ist von
DOM Storage aus dem kommenden Standard HTML 5. Das clientseitige Speichern wird damit denkbar einfach:
sessionStorage.vorname = "Stefan"; // Nur für die Browser-Session speichern globalStorage['selfhtml.org'].vorname = "Stefan"; // für unbestimmte Zeit speichern
Eine Implementierung dieses Entwurfes findet sich bisher nur im Firefox ab Version 2 (siehe dessen
Dokumentation).
Microsoft bietet über sogenannte DHTML Behaviours eine eigene proprietäre Speichermethode namens
userData, die bisher nur der hauseigene Internet Explorer implementiert. Auch darauf sei an dieser Stelle nur hingewiesen.
Schließlich sei eine Alternative zur Linearisierung von JavaScript-Daten nach dem Schema ?name1=wert1&name2=wert2&name3=wert3&... genannt. Wie gesagt unterstützen die vorgestellten Funktionen nur die Übermittlung von einfachen JavaScript-Werten, das heißt vor allem String-, Number- und Boolean-Werte. Komplexe Datentypen wie Arrays, Objects und reguläre Ausdrücke werden nicht korrekt kodiert und werden bei der Übertragung verstümmelt. Eine bessere Lösung wäre die Nutzung des Formates
JSON und den Funktionen
JSON.stringify() und JSON.parse(). Das vorgestellte Script storage.js kann einfach abgeändert werden, sodass die Daten mit JSON kodiert und dekodiert werden.
© 2007
Impressum, für diese Seite:
molily@selfhtml.org