Rheinwerk Computing <openbook>
Rheinwerk Computing - Programming the Net


JavaScript von Christian Wenz
Browserübergreifende Lösungen
JavaScript - Zum Katalog
gp Kapitel 11 Cookies
  gp 11.1 Was ist ein Cookie?
  gp 11.2 Wie sieht ein Cookie aus?
  gp 11.3 Cookies mit JavaScript
  gp 11.4 Informationen behalten ohne Cookies
  gp 11.5 Fragen & Aufgaben

Kapitel 11 Cookies

250 g Mehl, 150 g Butter oder Margarine, 60 g Zucker,
1 Ei oder 1 Eigelb, 2 EL kaltes Wasser, 1 Prise Salz.
– Zutatenliste für Plätzchenteig, aus: Sybil Gräfin Schönfeldt, Kochbuch für Kinder

Cookies sind böse. Man kann damit den Benutzer ausspionieren, seine E–Mail-Adresse und alle seine Passwörter herausbekommen. Über Cookies erhält eine Webseite Lese- und Schreibzugriff auf die lokale Festplatte des Benutzers. Cookies sind primär ein Ärgernis, und sie sollten permanent ausgeschaltet werden. Es gibt keine nützliche Anwendung. Hat man viele Cookies, so hat man automatisch auch viele kleine Dateien auf der Festplatte, und diese wird auf diese Weise zugemüllt, wertvoller Speicherplatz geht verloren. Andererseits sind Cookies eigentlich richtige Programme, Spionageprogramme, um genau zu sein. Der Papst ist evangelisch, und der Bär wohnt nicht im Wald.

Der letzte Satz ist frei erfunden, die Aussagen zuvor stammen jedoch allesamt aus der Presse, teilweise sogar aus der so genannten Fachpresse. An einigen der Aussagen ist entfernt etwas Wahres dran, manche sind bedingt gültig. Man kann jedoch Cookies nicht global als böse abkanzeln. In diesem Kapitel wird beschrieben, was ein Cookie wirklich ist, welche Möglichkeiten und welche Einschränkungen es gibt, und wie man sie mit JavaScript erstellen und nutzen kann.


Rheinwerk Computing

11.1 Was ist ein Cookie?  downtop

HTTP wird als stateless protocol (zustandsloses Protokoll) bezeichnet: Die Verbindung zum Server wird geöffnet, Browser und Server senden Daten (der Browser eine HTTP-Anfrage, der Server ein Dokument, eine Datei o. Ä.), und dann wird die Verbindung wieder gekappt. Es ist somit praktisch unmöglich, einen Benutzer auf mehreren Seiten einer Website zu verfolgen. Sieht man von einer Logdatei-Analyse ab, ist es bei mehreren parallelen Zugriffen praktisch unmöglich festzustellen, welche Anfrage von welchem Benutzer kam.

Dies ist nicht nur ein herber Schlag für eine ganze Berufgruppe, die Marketing-Leute, sondern auch für andere Bereiche ist es interessant, den Benutzer zu kennen. In einem virtuellen Kaufhaus muss auf jeder Seite der Kunde bekannt sein, oder zumindest muss die Applikation wissen, welche Artikel dieser Kunde in seiner Datenbank hat. In dem Kapitel »Fenster I« wurde eine Warenkorbanwendung mit Frames vorgestellt, aber Frames sind nicht jedermanns Sache. In diesem Kapitel wird noch eine weitere Lösung vorgestellt, die jedoch auch ihre Nachteile aufweist.

Cookies sind manchmal das geringste Übel. Ein Cookie ist prinzipiell eine Textinformation, die auf der Festplatte des Benutzers gespeichert wird. Bei jeder HTTP-Anfrage werden bestimmte Cookies des Benutzers mitgeschickt (dazu später mehr), und bei der HTTP-Antwort des Webservers können ebenfalls Cookies mitgeschickt werden. Der Netscape Navigator speichert Cookies zeilenweise in der Datei cookies.txt im jeweiligen Benutzerverzeichnis im Netscape Navigator, während die Datei beim Macintosh MagicCookie heißt. Der Microsoft Internet Explorer verwendet für jeden einzelnen Cookie eine einzelne Textdatei, außer bei manchen Versionen wie etwa 3.x für Windows 3.11, dort befinden sich die Cookies auch insgesamt in einer Datei.

gp  Netscape selbst hat noch zu Zeiten der Version 1.1 Cookies vorgeschlagen (implementiert wurden sie jedoch erst in Version 2.0). Die Spezifikation, damals wie heute gleich, finden Sie unter http://www.netscape.com/ newsref/std/cookie_spec.html. Cookies werden vom Internet Explorer ab Version 3 unterstützt – mit JavaScript im lokalen Gebrauch jedoch erst ab Version 4 (auf einem Webserver schon ab Version 3). Es gibt einige, von Netscape aufgelegte Einschränkungen bei Cookies, die zum Großteil auch nur für den Netscape Navigator gelten. Um die Browserkompatibilität zu wahren, sollte man sich jedoch generell daran halten. Ein Cookie darf höchstens 4 KB an Daten enthalten (in der Realität sind es jedoch nur etwa 2 000 Zeichen), und der Browser kann nur 300 Cookies verwalten. Sobald der Benutzer den 301. Cookie enthält, wird dafür ein anderer (in der Regel der älteste) gelöscht. Cookies werden, wie bereits erwähnt, vom Browser gespeichert und verwaltet; wenn Sie mehrere Browser benutzen, speichert jeder seine eigenen Cookies, Sie können Cookies also nicht browser-übergreifend verwenden.

Rheinwerk Computing

11.2 Wie sieht ein Cookie aus?  downtop

Ein Cookie erinnert an den Versand eines Formulars via GET. Jeder Cookie hat als Hauptkomponente (mindestens) ein Name-Wert-Paar. Auch hier gelten wieder die üblichen Regeln der URL-Codierung, also heißt es Autor=Christian+Wenz oder Autor=Christian%20Wenz, weil das Plus-Zeichen oder hexadezimal 20 (dezimal 32) das Leerzeichen ersetzt. Außerdem hat ein Cookie noch die folgenden weiteren, optionalen Parameter in Form von Name-Wert-Paaren:

gp  domain
Dieser Parameter bezeichnet die Domain des Servers, der den Cookie lesen darf. Standardmäßig ist das der Server-Anteil der URL der Seite, die den Cookie setzt. Dieses Attribut wird jedoch unter bestimmten Umständen anders gesetzt. Angenommen, eine Firma hat mehrere Webserver, server1.firma.de, server2.firma.de und www.firma.de. Von jedem dieser Server aus soll ein Cookie gelesen werden können. Standardmäßig wäre der domain-Wert des Cookies, wenn er auf www.firma.de/cookie.htm gesetzt worden ist, www.firma.de; als direkte Konsequenz hätten server1.firma.de und server2.firma.de keinen Zugriff auf diesen Cookie. Um dies zu vermeiden, kann das domain-Attribut gesetzt werden. Es würde in diesem Fall auf .firma.de gesetzt, wodurch jeder Server, dessen Name auf .firma.de endet, den Cookie lesen kann. Ein Domainname muss hier mindestens zwei Punkte enthalten, deswegen steht ein Punkt vor firma.de. Einige Leute sind auf die Idee gekommen, einfach die Cookies mehrerer (unterschiedlicher) Seiten auf einem zentralen Cookie-Server zu speichern. Beim Netscape Navigator gibt es eine Option, die dies vereitelt (Bearbeiten/Einstellungen/Erweitert/Nur an den ursprünglichen Server zurückgesendete Cookies akzeptieren). Pro Domain werden übrigens nur 20 Cookies akzeptiert, danach werden alte Cookies gelöscht bzw. überschrieben.
gp  expires
Jeder Cookie hat ein Ablaufdatum, nach dem er gelöscht wird. Manche Seiten setzen dieses Datum auf das Jahr 2037, und so lange wird die Festplatte des Benutzers sicher nicht halten – es sind also (beinahe) unendlich lange gültige Cookies möglich. Eine Sicherheit, dass der Cookie tatsächlich so lange hält, hat man indes nicht, da Cookies manuell oder durch Überschreitung der 300er-Grenze vom Netscape Navigator gelöscht werden können. Wird kein Ablaufdatum angegeben, so wird der Cookie gelöscht, sobald der Browser beendet wird. Man spricht hier von einem Session-Cookie oder einem temporären Cookie; Cookies mit gesetztem Ablaufdatum nennt man permanente Cookies. Das Ablaufdatum sollte im GMT-Format angegeben werden; in JavaScript ist das ein Fall für die toGMTString()-Methode.
gp  path
Wie oben ausgeführt, kann ein Cookie von demjenigen Server ausgelesen werden, der in der domain-Eigenschaft angegeben ist. Wenn wichtige Daten in Cookies gespeichert werden, gibt es jedoch unter Umständen ein Problem. Wenn Sie bei Ihrem ISP eine Website hosten lassen, haben Sie oft eine URL der Bauart mitglied.isp.de/mitgliedsname. Die Seite des Konkurrenten, die auch bei diesem ISP gehostet wird, hat die URL mitglied.isp.de/nocheinmitglied. Die Domain ist jedoch in beiden Fällen mitglied.isp.de – und damit kann die Website Ihres Konkurrenten die Cookies lesen, die Ihre Seite bei dem Benutzer gesetzt hat (wenn der Benutzer zuerst Ihre Seiten und dann die der Konkurrenz besucht). Aus diesem Grund kann man noch einen Pfad setzen. Nur Webseiten, die in diesem Pfad auf dem entsprechenden Server liegen (Unterverzeichnisse eingeschlossen), können den Cookie lesen. In diesem Falll sollten Sie also den Pfad Ihrer Cookies auf /mitgliedsname setzen, um böse Überraschungen zu vermeiden. Ein weiteres Beispiel: Der Standardwert von path ist das Verzeichnis der Datei auf dem Webserver, die den Cookie setzt. Wenn Sie den Cookie aber auf jeder anderen Seite, auch in anderen Verzeichnissen, lesen wollen, sollten Sie den Wert von path auf / setzen.
gp  secure
Diese Eigenschaft muss nur angegeben werden, hier liegt kein Name-Wert-Paar vor. Ist die Eigenschaft gesetzt, so darf der Cookie nur gelesen werden, wenn man mit einer HTTPS-Verbindung, also mit einer »sicheren«, verschlüsselten Verbindung mit der Webseite verbunden ist.

Die Parameter bzw. Name-Wert-Paare werden durch Semikolon und Leerzeichen voneinander getrennt. Ein typischer Cookie sieht also folgendermaßen aus:

Autor=Christian+Wenz; domain=.galileo-press.de; 
expires=Sat, 01-Mon-2000 12:00:00 GMT; path=/; secure

Der Name des Cookies ist gleichzeitig der Identifikator, es darf also nicht zwei Cookies mit demselben Namen geben. Wird ein Cookie gesetzt, den es (vom Namen her) schon gibt, wird der alte durch die neuen Daten überschrieben. Nur wenn der Pfad unterschiedlich ist, kann man denselben Namen öfter verwenden. Dieses Vorgehen ist jedoch nicht empfehlenswert – und auch mit JavaScript schwer zu fassen. Das Setzen ist einfach, aber das Lesen gestaltet sich schwierig.


Rheinwerk Computing

11.3 Cookies mit JavaScript  downtop

Cookies werden mit JavaScript in der Eigenschaft cookie des document-Objekts gespeichert, und zwar als Zeichenkette. Das impliziert, dass man auf Cookies nicht wie auf Objekte mit Eigenschaften und Methoden zugreifen kann. Beim Setzen des Cookies ist das kein weiteres Problem, da denkt der JavaScript-Interpreter mit. Wenn der Wert von document.cookie beispielsweise

"Autor=Christian+Wenz"

ist, und der Befehl

document.cookie = "Verlag=Galileo"

ausgeführt wird, lautet der Wert von document.cookie danach folgendermaßen:

Autor=Christian+Wenz; Verlag=Galileo

Der JavaScript-Interpreter passt den Wert des Cookies also automatisch an. Das gilt auch, wenn der Wert eines Cookies geändert wird. Nach der Ausführung des Befehls

document.cookie="Verlag=Galileo-Press"

wird der Wert von document.cookie in

Autor=Christian+Wenz; Verlag=Galileo-Press

geändert.


Rheinwerk Computing

11.3.1 Cookies setzen  downtop

Es bietet sich an, zum Setzen eines Cookies eine Funktion zu schreiben, an die die einzelnen Parameter des Cookies als Funktionsparameter übergeben werden. Im Folgenden ist so eine Funktion aufgeführt: Es wird überprüft, ob der jeweilige Parameter den Wert null hat (also nicht angegeben worden ist), und dementsprechend wird der Cookie angepasst.

function setCookie(name, wert, domain, expires, path, secure){
   var cook = "name="+unescape(wert)
   cook += (domain) ? "; domain="+ domain : ""
   cook += (expires) ? "; expires="+expires : ""
   cook += (path) ? "; path="+path : ""
   cook += (secure) ? "; secure"
   document.cookie = cook
}

Der Beispielaufruf

setCookie("Autor", "Christian Wenz", null, ((new Date()).
  getTime()+1000*3600*24).toGMTString())

setzt einen Cookie Autor=Christian+Wenz, der einen Tag lang gültig ist (1000 Millisekunden pro Sekunde, 3600 Sekunden pro Stunde, 24 Stunden pro Tag).


Rheinwerk Computing

11.3.2 Cookies löschen  downtop

Das Löschen von Cookies ist auch sehr bequem. Man versetzt hierbei das Ablaufdatum in die Vergangenheit. Damit wird der Cookie danach sofort ungültig und gelöscht. Zwar könnte man das aktuelle Datum nehmen, und einen Tag oder gar nur eine Sekunde davon abziehen, aber in der Regel setzt man das Ablaufdatum auf die frühestmögliche Zeit unter JavaScript, den 1. Januar 1970, eine Sekunde nach Mitternacht (hat unter anderem den Grund, dass Netscape 2 Cookies, deren Ablaufdatum nur eine Millisekunde zurückgesetzt worden ist, nicht gelöscht hat). In GMT-Schreibweise sieht das so aus:

Thu, 01-Jan-70 00:00:01 GMT

Beim Schreiben einer Funktion sollte man die Parameter für die Domain und den Pfad ebenfalls mit angeben können, um gegebenenfalls den richtigen Cookie zu löschen:

function eraseCookie(name, domain, path){
   var cook="name=; expires=Thu, 01-Jan-70 00:00:01 GMT"
   cook += (domain) ? "domain="+domain : ""
   cook += (path) ? "path="+path : ""
   document.cookie = cook
}

Rheinwerk Computing

11.3.3 Cookies lesen  downtop

Kommen wir zum schwierigsten Teil: den Wert eines Cookies wieder zu lesen. Wie bereits zuvor angedeutet, wird der Sonderfall zweier gleichnamiger Cookies mit unterschiedlicher Pfadangabe hier nicht berücksichtigt, weil der Aufwand unverhältnismäßig steigt. Statt dessen wird der zu schreibenden Funktion, die den Wert eines Cookies zurückgeben soll, nur der Name des Cookies übergeben, sonst nichts. Der erste Cookie, der dann gefunden wird, wird angezeigt.

Bei dieser Aufgabenstellung sieht man erst den Nachteil, dass man auf Cookies nicht als Objekte zugreifen kann. Der Wert des Cookies muss mit profanen Stringfunktionen herausgefunden werden. Das ist aber recht einfach: Zuerst wird nach der Zeichenkette "name=" gesucht, wobei name den Namen des Cookies angibt. Dahinter steht dann der Wert des Cookies. Sobald man auf ein Semikolon oder das Ende der Zeichenkette stößt, hat man den gesamten Wert, und kann ihn (aus der URL-Schreibweise wieder zurückcodiert) zurückgeben.

function getCookie(name){
   var i=0  //Suchposition im Cookie
   var suche = name+"="
   while (i<document.cookie.length){
      if (document.cookie.substring(i, i+suche.length)==suche){
         var ende = document.cookie.indexOf(";", i+suche.length)
         ende = (ende>-1) ? ende : document.cookie.length
         var cook = document.cookie.substring(i+suche.length, ende)
         return unescape(cook)
      }
      i++
   }
   return null
}

Rheinwerk Computing

11.3.4 Cookie-Unterstützung überprüfen  downtop

Aufgrund der eingangs angedeuteten Paranoia bezüglich Cookies haben viele Benutzer die Verwendung von Cookies abgeschaltet, oder sie lassen ein Warnfenster ausgeben, sobald ein Cookie vom Server (oder hier vom Browser selbst) geschickt wird.

Abbildung 11.1  Die Cookie-Warnung des Netscape Navigator
Abbildung

Aus diesem Grund sollte man – zumindest am Anfang einer auf Cookies basierenden Applikation – prüfen, ob Cookies unterstützt werden. An vielen Stellen wurde vorgeschlagen, folgende Überprüfung vorzunehmen:

if (document.cookie){
   // Cookie-Code
}

Prinzipiell ist der Ansatz gar nicht schlecht, aber oft wurde einfach behauptet, so werde überprüft, ob das Cookie-Objekt unterstützt wird.

Das ist leider völlig falsch, denn document.cookie ist kein Objekt, sondern einfach eine (sehr funktionsfähige, man denke an die Zuweisungen) Zeichenkette. Aus diesem Grund ist hier die if-Abfrage nur eine Abkürzung für folgenden Code:

if (document.cookie.length>0){
   // Cookie-Code
}

Der Grund, warum die erstere Variante recht verbreitet ist, besteht darin, dass es vielleicht einmal Browser gibt, bei denen document.cookie gleich null ist – und dann führt ein Zugriff auf die Eigenschaft length zu einem Fehler.

Aber zurück zum Thema. Die obigen Abfragen überprüfen, ob document.cookie mindestens ein Zeichen enthält. Aus diesem Grund kann man mit obiger Methode also nicht testen, ob der Browser Cookies unterstützt, sondern nur, ob schon ein Cookie gesetzt worden ist. Sie müssen also einen Cookie setzen, um herauszufinden, ob der Browser ihn annimmt.

Eine Funktion, die überprüft, ob der Benutzer Cookies überhaupt akzeptiert, ist Folgende:

function checkCookie(){
   setCookie("CookieTest", "OK")
   if (!getCookie("CookieTest"))
      return false
   else{
      eraseCookie("CookieTest")
      return true
   }
}

Hier sehen Sie einen Vorteil von JavaScript gegenüber serverseitigen Programmiersprachen: Bei der zuletzt dargestellten Vorgehensweise kann man nur übermittelte Cookies auslesen und dann neue Cookies setzen. Um aber nach dem Setzen zu überprüfen, ob das auch funktioniert hat, muss man eine neue Seite aufrufen bzw. ein neues Skript ausführen.

Diese Funktion sollten Sie jedoch nicht allzu oft ausführen. Viele Benutzer lassen sich einfach eine Warnmeldung anzeigen, um beispielsweise zu sehen, was in dem Cookie steht. Auf vielen Seiten benötigen Sie zwar beispielsweise den Cookie der Site selbst (beispielsweise Ihre Kundennummer), nicht aber den Cookie des Werbebanners auf derselben Site. Wenn Sie nun diese Funktion auf jeder Seite ausprobieren, ärgern sich die Benutzer über die vielen Warnmeldungen.

Abbildung 11.2  Der Internet Explorer 5 bietet detaillierte
Einstellungsmöglichkeiten für Cookies
Abbildung

gp  In diesem Zusammenhang ein kleiner Hinweis: Paranoia hin oder her, die Spionagegefahr bei temporären Cookies ist wirklich gering. Außerdem benötigt beispielsweise Hotmail unbedingt temporäre Cookies, um zu funktionieren (Yahoo! Mail erforderte zeitweise sogar permanente Cookies, inzwischen benötigt man nur noch temporäre Cookies). Der Internet Explorer 5 unterscheidet zwischen diesen beiden Arten von Cookies, und eine empfehlenswerte Einstellung ist, temporäre Cookies immer anzunehmen, bei permanenten Cookies jedoch nachzufragen.

Rheinwerk Computing

11.3.5 Warenkorb mit Cookies  downtop

Um das Überprüfen auf und den Einsatz von Cookies einmal an einem praktischen Beispiel vorzuführen, soll das Beispiel mit dem Warenkorb auf Cookies umgestellt werden. Das Frameset wird komplett aufgelöst, es gibt nur noch eine Hauptseite. Auf jeder Artikelseite wird dann ein Link auf den Warenkorb angelegt. Die Formular-Übergabe an den Warenkorb sieht folgendermaßen aus:

<HTML>
<HEAD><TITLE>Beispielsartikel für den Warenkorb</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
/***
   Hier alle Cookie-Funktionen von oben einfügen
***/
function istZahl(n){
  s = ""+n
  var ziffern = "0123456789"
  for (var i=0; i<s.length; i++)
    if (ziffern.indexOf(s.charAt(i))<0) return false
  return true
}
function einfuegen(a, p, b){
   var anz = (istZahl(a) ? a : "0")
   var anzahl = getCookie("Anzahl") + anz + "|"
   var preis = getCookie("Preis") + p+ "|"
   var beschreibung = getCookie("Beschreibung") + b + "|"
   setCookie("Anzahl", anzahl)
   setCookie("Preis", preis)
   setCookie("Beschreibung", beschreibung)
   if (!document.cookie)
      alert("Aktivieren Sie Cookies in Ihrem Browser!")
   else
      alert("Artikel in Warenkorb eingefügt")
} //--></SCRIPT> </HEAD> <BODY> <H3>M. Proust I</H3> Das 1. Werk des französischen Autors; 20,55 Euro <FORM> <INPUT TYPE="Anzahl" SIZE=2 VALUE="1"> Stück  <INPUT TYPE="BUTTON" VALUE="In den Warenkorb" onClick=" einfuegen   (this.form.Anzahl.value, '20.55', 'Proust I.')"> </FORM> </BODY> </HTML>

Die Seite, die den Warenkorb ausgibt, ändert sich folgendermaßen:

<HTML>
<HEAD><TITLE>Ausgabe des Warenkorbs</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
/***
   Hier alle Cookie-Funktionen von oben einfügen
***/
var a = getCookie("Anzahl") + ""
var p = getCookie("Preis") + ""
var b = getCookie("Beschreibung") + ""

function naechste_anzahl(){    if (a.indexOf("|")==-1) return ""    var anz = a.substring(0, a.indexOf("|"))    a = a.substring(a.indexOf("|")+1)    return anz } function naechster_preis(){    if (p.indexOf("|")==-1) return ""    var pr = p.substring(0, p.indexOf("|"))    p = p.substring(p.indexOf("|")+1)    return pr } function naechste_beschreibung(){    if (b.indexOf("|")==-1) return ""    var bes = b.substring(0, b.indexOf("|"))    b = b.substring(b.indexOf("|")+1)    return bes } //--></SCRIPT> </HEAD> <BODY> <H3>Inhalt des Warenkorbs</H3> <SCRIPT LANGUAGE="JavaScript"><!-- function tag(s){ document.write("<"+s+">") } tag("TABLE BORDER=1"); tag("TR"); tag("TD"); document.write("Anzahl"); tag("/TD") tag("TD"); document.write("Beschreibung"); tag("/TD") tag("TD"); document.write("Einzelpreis"); tag("/TD") tag("TD"); document.write("Gesamtpreis"); tag("/TD") tag("/TR") anzahl="0" while (anzahl!=""){   var anzahl = naechste_anzahl()
  var preis = naechster_preis()
  var beschreibung = naechste_beschreibung()
  if (anzahl!=""){     tag("TR")     tag("TD"); document.write(anzahl); tag("/TD")     tag("TD"); document.write(beschreibung); tag("/TD")     tag("TD"); document.write(preis); tag("/TD")     tag("TD"); document.write(anzahl*preis); tag("/TD")     tag("/TR")   } } tag("/TABLE") //--></SCRIPT> </BODY> </HTML>

Sie sehen zum einen, dass sich die Änderungen im überschaubaren Rahmen halten. Sie sehen jedoch auch zum anderen, dass recht viel JavaScript-Code verwendet wird, insbesondere auf allen Artikelseiten. Dies lässt sich jedoch durch die Verwendung von Server Side Includes (SSI) vermeiden, oder – wenn es die Browser-Ausstattung Ihres Publikums erlaubt, aber wirklich nur dann – durch <SCRIPT SRC="...">.

Wenn man keine serverseitige Programmiersprachen-Unterstützung zur Verfügung hat, kann man über Cookies den Inhalt des Warenkorbs speichern. Die meisten Online-Shops, inklusive Amazon, speichern zumindest die User-ID des Benutzers in einem Cookie. Bei Amazon ist das optional, bei My-World beispielsweise geht es nicht ohne.

Andere Shops jedoch, beispielsweise Primus-Online, kommen vollständig ohne Cookies aus. Wie das möglich ist, und wie das Warenkorbbeispiel entsprechend umgeschrieben werden kann, zeigt der nächste Abschnitt.


Rheinwerk Computing

11.4 Informationen behalten ohne Cookies  downtop

Wenn man sowohl auf Frames als auch auf die Verwendung von Cookies als auch auf andere, eher exotische Methoden der Datenhaltung verzichten will, hat man noch eine Möglichkeit, um Informationen zwischen HTML-Seiten zu übergeben. Der Query-String einer URL, also die Zeichenkette hinter dem Fragezeichen, ist bei statischen Seiten völlig unerheblich, man kann also (fast) alles angeben, was man will. Unser Ziel soll es sein, in dieser Zeichenkette alle notwendigen Informationen zu übergeben. Diese Vorgehensweise hat jedoch – zum Teil besonders im vorliegenden Beispiel – drei Nachteile:

gp  Jeder einzelne Link muss angepasst werden. Sollte der Benutzer auf einen Link klicken, der unvollständig ist (also ohne Query-String), sind alle Informationen verloren; auch beim Betätigen der Zurück-Schaltfläche im Browser ist das der Fall (war es bei der Frames-Lösung für den Warenkorb jedoch auch).
gp  Die Kommandozeile ist – je nach Browser, Betriebssystem und Version – auf etwa 1 000 Zeichen begrenzt.
gp  Der Query-String steht in location.search, und diese Eigenschaft wird im Internet Explorer erst ab Version 4 bei lokalem Zugriff unterstützt, beim IE3 nur dann, wenn die Website auf einem Webserver und nicht auf der heimischen Festplatte liegt. Beachten Sie das beim Testen!

Bei einigen Anwendungen ist das nicht weiter tragisch. Stellen Sie sich vor, ein Benutzer muss ein längeres (Bestell-)Formular ausfüllen, und bekommt nach dem Abschicken des Formulars einen grafisch leicht aufbereiteten Bestellschein, der nur noch gedruckt und gefaxt oder eingeschickt werden muss. Hier sind Cookies oder Frames, sofern sie nicht eh schon im Site-Design vorhanden sind, zu viel des Guten; die Kommandozeile tut es auch.

Eine Artikelseite unseres kleinen Kaufhauses sieht – bei Verwendung der URL als Übergabe für die Artikel – folgendermaßen aus:

<HTML>
<HEAD><TITLE>Beispielartikel für den Warenkorb</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!-
function goto(url){
   //generiert geeigneten Link
}
function getValue(name){
  //gibt den Wert der Variablen "name" in der URL zurück
}
function istZahl(n){
  s = ""+n
  var ziffern = "0123456789"
  for (var i=0; i<s.length; i++)
    if (ziffern.indexOf(s.charAt(i))<0) return false
  return true
}
function einfuegen(a, p, b){
   var anz = (istZahl(a) ? a : "0")
   var anzahl = getValue("Anzahl") + anz + "|"
   var preis = getValue("Preis") + p+ "|"
   var beschreibung = getValue("Beschreibung") + b + "|"
   var seite = "bestaetigung.html?"
   seite += "anzahl="+encode(anzahl)
   seite += "&preis="+encode(preis)
   seite += "&beschreibung="+encode(beschreibung)
   location.href = seite
}
//--></SCRIPT>
</HEAD>
<BODY>
<H3>M. Proust I</H3>
Das 1. Werk des französischen Autors; 20,55 Euro
<FORM>
<INPUT TYPE="Anzahl" SIZE=2 VALUE="1"> Stück 
<INPUT TYPE="BUTTON" VALUE="In den Warenkorb" onClick=" einfuegen
  (this.form.Anzahl.value, '20.55', 'Proust I.')">
</FORM>
</BODY>
</HTML>

Die Datei bestaetigung.html gibt eine Bestätigung aus und bietet einige Links zu weiteren Artikeln an.

<HTML>
<HEAD><TITLE>Warenkorb</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
function goto(url){
   //generiert geeigneten Link
}
//-->
</HEAD>
<BODY>
<H3>Artikel ist im Warenkorb eingetragen!</H3>
<A HREF="javascript:goto('artikel1.html')">Noch ein Artikel</A><BR>
<A HREF="javascript:goto('artikel2.html')">Ein weiterer Artikel</A>
</BODY>
</HTML>

Die Ausgabeseite liest schließlich aus der URL die einzelnen Werte aus und verfährt ansonsten wie gehabt:

<HTML>
<HEAD><TITLE>Ausgabe des Warenkorbs</TITLE>
<SCRIPT LANGUAGE="JavaScript"><!--
function getValue(name){
   //gibt den Wert der Variablen "name" in der URL zurück
}
var a = getValue("Anzahl")
var p = getValue("Preis")
var b = getValue("Beschreibung")

function naechste_anzahl(){
   if (a.indexOf("|")==-1) return ""
   var anz = a.substring(0, a.indexOf("|"))
   a = a.substring(a.indexOf("|")+1)
   return anz
}
function naechster_preis(){
   if (p.indexOf("|")==-1) return ""
   var pr = p.substring(0, p.indexOf("|"))
   p = p.substring(p.indexOf("|")+1)
   return pr
}
function naechste_beschreibung(){
   if (b.indexOf("|")==-1) return ""
   var bes = b.substring(0, b.indexOf("|"))
   b = b.substring(b.indexOf("|")+1)
   return bes
}
//--></SCRIPT>
</HEAD>
<BODY>
<H3>Inhalt des Warenkorbs</H3>
<SCRIPT LANGUAGE="JavaScript"><!--
function tag(s){ document.write("<"+s+">") }
tag("TABLE BORDER=1");
tag("TR");
tag("TD"); document.write("Anzahl"); tag("/TD")
tag("TD"); document.write("Beschreibung"); tag("/TD")
tag("TD"); document.write("Einzelpreis"); tag("/TD")
tag("TD"); document.write("Gesamtpreis"); tag("/TD")
tag("/TR")
anzahl="0"
while (anzahl!=""){
  var anzahl = naechste_anzahl()
  var preis = naechster_preis()
  var beschreibung = naechste_beschreibung()
  if (anzahl!=""){
    tag("TR")
    tag("TD"); document.write(anzahl); tag("/TD")
    tag("TD"); document.write(beschreibung); tag("/TD")
    tag("TD"); document.write(preis); tag("/TD")
    tag("TD"); document.write(anzahl*preis); tag("/TD")
    tag("/TR")
  }
}
tag("/TABLE")
//--></SCRIPT>
</BODY>
</HTML>

Damit das Beispiel funktioniert, fehlen noch zwei Funktionen, die gemeinerweise in den obigen Listings immer nur leer angegeben wurden:

gp  goto(url): Die Seite, deren Adresse im Parameter url steht, wird aufgerufen. Alle Links innerhalb der Warenkorbapplikation müssen über diese Funktion laufen!
gp  getValue(name): Der Wert der Variablen name in der URL wird ausgelesen und zurückgegeben.

Von der Implementierung her ist goto() die einfachere Funktion. Es muss die neue Seite aufgerufen, dabei aber unbedingt die Kommandozeile beibehalten werden. Das erreicht man im Allgemeinen durch das Anhängen von location.search an die URL. Sollte die URL der neuen Seite jedoch schon ein Fragezeichen enthalten, so wird das erste Zeichen von location.search, das Fragezeichen, abgetrennt und der Rest mit einem kaufmännischen Und (&) an die URL angehängt. Dieser Fall tritt in unserem Beispiel nicht auf, aber es kann ja sein, dass Sie in der Kommandozeile weitere Informationen weitergeben – und diese auf analoge Art und Weise verwerten – wollen.

function goto(url){
   if (url.indexOf("?")<0)
      location.href = url + location.search
   else {
      var parameter = location.search.substring(1,
        location.search.length)
      location.href = url + "&" + parameter
   }
}

Die Programmierung der Funktion getValue() sieht auf den ersten Blick etwas schwieriger aus, aber von der Vorgehensweise ist sie mit der Funktion getCookie() vergleichbar. In diesem Fall ist das Trennzeichen nicht das Semikolon, sondern das kaufmännische Und. Das war dann aber auch der einzige Unterschied; ansonsten muss praktisch nur noch document.cookie durch location.search ersetzt werden.

function getValue(name){
   var i=1  //Suchposition in der URL
   var suche = name+"="
   while (i<location.search.length){
      if (location.search.substring(i, i+suche.length)==suche){
         var ende = location.search.indexOf("&", i+suche.length)
         ende = (ende>-1) ? ende : location.search.length
         var loca = location.search.substring(i+suche.length, ende)
         return unescape(loca)
      }
      i++
   }
   return ""
}

Rheinwerk Computing

11.5 Fragen & Aufgaben  toptop

1. Wie werden Cookies prinzipiell eingeteilt?
2. Erzeugen Sie – sofern möglich – eine Seite, die angibt, wie oft der Benutzer mit dem jeweiligen Browser schon auf der Seite war.
3. Erzeugen Sie – sofern möglich – eine Seite, die angibt, wie oft der Benutzer mit irgendeinem Browser schon auf der Seite war.
4. Ein Benutzer kann die Unterstützung von Cookies entweder ausschalten, eine Warnmeldung einblenden lassen oder Cookies prinzipiell annehmen. Wie könnte man mit JavaScript feststellen, welche dieser drei Möglichkeiten der Benutzer gewählt hat? Setzen Sie Ihre Idee in Code um.
5. Schreiben Sie das erste Beispielformular aus »Formulare I« folgendermaßen um: Das Formular wird ohne Vollständigkeitsüberprüfung via GET abgeschickt. Auf der nächsten Seite erhält der Benutzer, wenn das Formular nicht vollständig ausgefüllt worden ist, einen Link zurück auf das Formular, wobei der Query-String beibehalten wird. Das Formular aus der vorherigen Aufgabe soll, wenn der Benutzer auf den besonderen Link klickt, vorausgefüllt sein, und zwar mit den Angaben, die der Benutzers bei seinem ersten Versuch eingegeben hat. Außerdem sollen die Namen der fehlenden Felder ausgegeben werden.





1    Im englischen geläufiger Begriff: name-value-pair

2    Das nennt man auch Trial and Error.

3    Das muss auch so sein, da der Kundenkreis so groß ist, dass man sich Arroganz bei den Software-Voraussetzungen nicht erlauben kann.

4    Etwa als Variablen in einem anderen oder neuen Fenster; das fällt technisch unter die Kategorie der URLs.

  

JavaScript

jQuery

Einstieg in JavaScript

Responsive Webdesign

Suchmaschi-
nen-Optimie-
rung




Copyright © Rheinwerk Verlag GmbH 2001 - 2002
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken und speichern. 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.
Die Veröffentlichung der Inhalte oder Teilen davon bedarf der ausdrücklichen schriftlichen Genehmigung der Rheinwerk Verlag GmbH. Falls Sie Interesse daran haben sollten, die Inhalte auf Ihrer Website oder einer CD anzubieten, melden Sie sich bitte bei: >> Zum Feedback-Formular


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, fon: 0228.42150.0, fax 0228.42150.77, service@rheinwerk-verlag.de