Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 <<   zurück
JavaScript und AJAX von Christian Wenz
Das umfassende Handbuch
Buch: JavaScript und AJAX

JavaScript und AJAX
839 S., mit DVD, 39,90 Euro
Rheinwerk Computing
ISBN 3-89842-859-1
gp Kapitel 12 Cookies
  gp 12.1 Was ist ein Cookie?
  gp 12.2 Wie sieht ein Cookie aus?
  gp 12.3 Cookies mit JavaScript
    gp 12.3.1 Cookies setzen
    gp 12.3.2 Cookies löschen
    gp 12.3.3 Cookies lesen
    gp 12.3.4 Cookie-Unterstützung überprüfen
    gp 12.3.5 Ein Cookie statt vieler Cookies


Rheinwerk Computing

12.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.

Der Internet Explorer unterstützt auch Cookies, die nur vom Webserver gesetzt und ausgelesen werden können, jedoch nicht von JavaScript. Hinter diesen Cookies steht die Idee, kein Cookie-Auslesen per XSS zu ermöglichen (siehe Kapitel 30).


Rheinwerk Computing

12.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

12.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 sogar 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. In GMT-Schreibweise sieht das so aus:

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

Beim Schreiben einer Funktion müssen Sie 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

12.3.3 Cookies lesen  downtop

Kommen wir zum schwierigsten Teil: den Wert eines Cookies wieder zu lesen. Wie ich bereits zuvor angedeutet habe, wird der Sonderfall zweier gleichnamiger Cookies mit unterschiedlicher Pfadangabe hier nicht berücksichtigt, weil der Aufwand unverhältnismäßig steigt. Stattdessen 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 "";
}

Der zweite Parameter bei open() gibt die Position an, ab der gesucht werden soll. Wird kein zweiter Parameter angegeben (wie das bis dato bei allen Beispielen der Fall war), wird die Zeichenkette von Anfang an (also ab dem Zeichen mit der Position 0) durchsucht.


Rheinwerk Computing

12.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.

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
}

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 12.1     Die Cookie-Warnung eines Browsers

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
}

Die erste Variante ist deswegen recht verbreitet, weil 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 – also »Trial and Error«.

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.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 12.2     Ab Internet Explorer 5 gibt es detaillierte Einstellungsmöglichkeiten für Cookies – in anderen Browsern schon länger.

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.

In diesem Zusammenhang ein kleiner Hinweis: Paranoia hin oder her, die Spionagegefahr durch temporäre 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 sowie seine Nachfolgerversionen unterscheiden zwischen diesen beiden Arten von Cookies, und eine empfehlenswerte Einstellung ist, temporäre Cookies immer anzunehmen, bei permanenten Cookies jedoch nachzufragen. Bei neueren Netscape-Versionen ist sogar ein noch detaillierteres Cookie-Management möglich. Vor temporären Cookies muss aber wirklich niemand Angst haben.


Rheinwerk Computing

12.3.5 Ein Cookie statt vieler Cookies  toptop

Wie bereits mehrfach erwähnt wurde, ist die Anzahl von Cookies beschränkt, zumindest per Spezifikation: Es gibt 300 Cookies insgesamt, aber nur 20 Cookies pro Domain. Dieses Wissen scheint nicht weit verbreitet zu sein, da man oft sieht, dass manche Webmaster auf ihren Seiten mehrere Cookies setzen. In einem Cookie stehen 4 kByte zur Verfügung, also über 4000 Zeichen – ein Wert, der so gut wie nie ausgereizt wird. Es liegt also die Idee nahe, in einem einzelnen Cookie mehrere Informationen zu speichern.

Das Hauptproblem hierbei besteht darin, die Informationen in einem einzigen String zusammenzufassen. Andere Programmiersprachen bieten hierzu Hilfsfunktionen zum Zusammenfassen der Daten an (das nennt man unter anderem auch »Serialisierung«). Bei JavaScript müssen wir uns selbst behelfen.

Der hier vorgeschlagene Weg funktioniert wie folgt:

gp  Die Daten werden in einem assoziativen Array abgespeichert.
gp  Das assoziative Array wird wie folgt in einen String umgewandelt: Es wird ein neues Array erstellt, in das nacheinander die Schlüssel und dazugehörigen Werte des assoziativen Arrays eingefügt werden. Dieser Aufwand ist nötig, da die Methode toString() bei assoziativen Arrays eine leere Zeichenkette zurückliefert.
gp  Das Array wird dann mit der Methode toString() in eine Zeichenkette umgewandelt; das Ergebnis dient als Wert des Cookies.

Das Lesen von Informationen aus dem Cookie erfolgt ähnlich:

gp  Die Zeichenkette – gleichzeitig der Inhalt des Arrays – wird aus dem Cookie eingelesen.
gp  Mit der eval()-Methode wird die Zeichenkette in ein Array umgewandelt.
gp  Eine Schleife durchläuft das Array und erstellt daraus ein assoziatives Array.

Der Code ist in vier Funktionen aufgeteilt:

gp  Die Funktion laden_collection() lädt die Daten aus dem Cookie. Als Cookie-Name wird "daten" verwendet. Der Cookie wird eingelesen, und die Zeichenkette wird in ein Array umgewandelt; dazu werden die einzelnen Array-Elemente mit Anführungszeichen umgeben und eckige Klammern um das Ganze geschrieben:
str = str.replace(/,/g, "\",\""); //Anführungszeichen
str = "\"" + str + "\"" //Anführungszeichen
eval("temp = [" + str + "]");
Mit einer Schleife wird daraus das assoziative Array aufgebaut und zurückgegeben.
gp  Die Funktion lesen_collection() liest einen einzelnen Wert aus der Collection. Der Name des Wertes wird dabei als Parameter übergeben. Durch einen Aufruf von laden_collection() werden alle Cookie-Daten eingelesen und zurückgegeben; lesen_collection() muss dann nur noch den korrekten Wert auswählen und zurückgeben.
gp  Die Funktion speichern_collection() erwartet als Parameter ein assoziatives Array. Dieses Array wird zunächst in ein »normales« Array umgewandelt: Zunächst setzen Sie den Schlüssel, dann den dazugehörigen Wert, und das für alle Elemente:
for (var e in c) {
  temp.push(e);
  temp.push(c[e]);
}
Um eine maximale Browserunabhängigkeit mit alten Browsern (die kein push() kennen) zu erreichen, müssen Sie die Elemente wie folgt einfügen:
for (var e in c) {
  temp[temp.length] = e;
  temp[temp.length] = c[e];
}
Anfangs hat das Array keine Elemente, also die Länge 0. Das nächste Element hat daher den Index 0. Allgemein gesagt: Wenn Sie an ein Array a ein Element anfügen möchten, müssen Sie einfach a[a.length] setzen.Das resultierende Array wird mit toString() in eine Zeichenkette umgewandelt, und diese wird als Wert für den Cookie namens daten verwendet. Das Setzen des Cookies erfolgt mit einer (universellen) Funktion cookie_setzen().
gp  Die Funktion schreiben_collection() schließlich ändert ein einzelnes Element in der Collection. Als Parameter werden der Name des Elements und der gewünschte Wert übergeben. Die Funktion besteht nur aus drei Kommandos: Zunächst wird mit laden_collection() die Collection geladen, dann wird im resultierenden assoziativen Array der angegebene Wert gesetzt (oder geändert, sollte er bereits existieren). Das modifizierte Array wird an speichern_collection() übergeben und somit wieder im Cookie gespeichert.

Nach diesen detaillierten Vorüberlegungen schreiben sich die Funktionen wie von selbst; Sie finden sie im Folgenden abgedruckt und natürlich auch auf der DVD-ROM zum Buch.

function cookie_setzen() {
  var anzParameter = cookie_setzen.arguments.length;
  var parameter = cookie_setzen.arguments;
  // 1. Cookie-Name
  var name = parameter[0];
  // 2. Cookie-Wert
  var value = (anzParameter >= 2) ? parameter[1] : "";
  value = escape(value); // URL-Codierung
  // 3. Haltbarkeitsdatum
  var expires = (anzParameter >= 3) ? parameter[2] : null;
  if (expires != null) {
    if (expires.toGMTString) {
      expires = expires.toGMTString();
    }
  }
  // 4. Domain
  var domain = (anzParameter >= 4) ? parameter[3] : null;
  // 5. Pfad
  var path = (anzParameter >= 5) ? parameter[4] : null;

  if (path != null) {
    path = escape(path); // Sonderzeichen umwandeln
  }
  // 6. Sicherheitsstufe
  var secure = (anzParameter >= 6) ? parameter[5] : null;
  // Zusammensetzen des Cookies
  var c = name + "=" + escape(value);
  if (expires != null) {
    c += "; expires=" + expires;
  }
  if (domain != null) {

    c += "; domain=" + domain;
  }
  if (path != null) {
    c += "; path=" + path;
  }
  if (secure) {
    c += "; secure";
  }
  // Cookie setzen
  document.cookie = c;
}

function cookie_lesen(name) {
  var i = document.cookie.indexOf(name + "=");
  var c = "";
  if (i > –1) {
    var ende = document.cookie.indexOf("; ",
               i+name.length+1);
    if (ende == –1) {
      ende = document.cookie.length;
    }
    c = document.cookie.substring(i+name.length+1, ende);
  }
  return unescape(c);
}

function laden_collection() {
   var str = cookie_lesen("daten");
   str = unescape(str);
   var temp = new Array();
   // Daten aus dem Cookie in ein Array umwandeln
   if (str != "") {
      str = str.replace(/,/g, "\",\"");
      str = "\"" + str + "\""
      eval("temp = [" + str + "]");
   }
   // assoziatives Array erstellen
   var c = new Array();
   for (var i=0; i<temp.length; i+=2) {
     c[temp[i]] = temp[i+1];
   }
   // Array zurückgeben
   return c;
}

function lesen_collection(name) {
   var c = laden_collection();
   return c[name];
}

function speichern_collection(c) {
   var temp = new Array();
   for (var e in c) {
      temp[temp.length]=e;
      temp[temp.length]=c[e];
   }
   cookie_setzen("daten", temp.toString());
}

function schreiben_collection(name, wert) {
   var c = laden_collection();
   c[name] = wert;
   speichern_collection(c);
}

Zur Verdeutlichung folgt hier noch ein kleines Beispiel:

<html>
<head>
<title>Cookie-Collection</title>
</head>
<body>
<script type="text/javascript"><!--
function cookie_setzen() {
   var anzParameter = cookie_setzen.arguments.length;
   var parameter = cookie_setzen.arguments;
   // 1. Cookie-Name
   var name = parameter[0];
   // 2. Cookie-Wert
   var value = (anzParameter >= 2) ? parameter[1] : "";
   value = escape(value); // URL-Codierung
   // 3. Haltbarkeitsdatum
   var expires = (anzParameter >= 3) ? parameter[2] : null;
   if (expires != null) {
      if (expires.toGMTString) {
         expires = expires.toGMTString();
      }
   }
   // 4. Domain
   var domain = (anzParameter >= 4) ? parameter[3] : null;

   // 5. Pfad
   var path = (anzParameter >= 5) ? parameter[4] : null;

   if (path != null) {
      path = escape(path); // Sonderzeichen umwandeln
   }
   // 6. Sicherheitsstufe
   var secure = (anzParameter >= 6) ? parameter[5] : null;
   // Zusammensetzen des Cookies
   var c = name + "=" + escape(value);
   if (expires != null) {
      c += "; expires=" + expires;
   }
   if (domain != null) {
      c += "; domain=" + domain;
   }
   if (path != null) {
      c += "; path=" + path;
   }
   if (secure) {
      c += "; secure";
   }
   // Cookie setzen
   document.cookie = c;
}

function cookie_lesen(name) {
   var i = document.cookie.indexOf(name + "=");
   var c = "";
   if (i > –1) {
      var ende = document.cookie.indexOf("; ",
                 i+name.length+1);
      if (ende == –1) {
         ende = document.cookie.length;
      }
      c = document.cookie.substring(i+name.length+1, ende);
   }
   return unescape(c);
}

function laden_collection() {
   var str = cookie_lesen("daten");
   str = unescape(str);
   var temp = new Array();
   // Daten aus dem Cookie in ein Array umwandeln
   if (str != "") {
      str = str.replace(/,/g, "\",\"");
      str = "\"" + str + "\""
      eval("temp = [" + str + "]");
   }
   // assoziatives Array erstellen
   var c = new Array();
   for (var i=0; i<temp.length; i+=2) {
     c[temp[i]] = temp[i+1];
   }
   // Array zurückgeben
   return c;
}

function lesen_collection(name) {
   var c = laden_collection();
   return c[name];
}

function speichern_collection(c) {
   var temp = new Array();
   for (var e in c) {
      temp[temp.length]=e;
      temp[temp.length]=c[e];
   }
   cookie_setzen("daten", temp.toString());
}

function schreiben_collection(name, wert) {
   var c = laden_collection();
   c[name] = wert;
   speichern_collection(c);
}

   schreiben_collection("Autor", "Christian Wenz");
   schreiben_collection("Verlag",
      "Galileo Computing");
   schreiben_collection("Verlag", "Galileo Press");
   var autor = lesen_collection("Autor");
   var verlag = lesen_collection("Verlag");
   document.write("Autor: " + autor + "<br />\n");
   document.write("Verlag: " + verlag + "\n");
//--></script>
</body>
</html>

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 12.3     Die Werte werden aus der Collection ausgelesen und ausgegeben.

Sie sehen an diesem Beispiel, dass bereits vorhandene Einträge im Cookie direkt überschrieben werden. Es gibt also keine zwei Einträge namens »Verlag«, sondern beim zweiten Setzen des Eintrags »Verlag« wird der erste Eintrag überschrieben.

 <<   zurück
  
  Zum Rheinwerk-Shop
Neuauflage: JavaScript
Neuauflage: JavaScript
bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: jQuery






 jQuery


Zum Rheinwerk-Shop: Einstieg in JavaScript






 Einstieg in JavaScript


Zum Rheinwerk-Shop: Responsive Webdesign






 Responsive Webdesign


Zum Rheinwerk-Shop: Suchmaschinen-Optimierung






 Suchmaschinen-
 Optimierung


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo




Copyright © Rheinwerk Verlag GmbH 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.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern