10.2 Auf Daten von Frames zugreifen
Wie ich bereits erwähnt habe, werden Frames in JavaScript wie Fenster behandelt. Mit den Schlüsselwörtern this und self erhält man also eine Referenz auf den aktuellen Frame – oder eben auf das aktuelle Fenster, wenn die Seite keine Frames enthält.
Auf sich selbst zuzugreifen ist aber nicht sonderlich interessant, und erst recht nichts Neues. Viel eher ist es wichtig, wie man von einer Webseite aus auf die Unterframes zugreifen kann und wie man – von einem Frame aus – auf das in der Hierarchie eine Stufe über einem stehende Element zugreifen kann. Um das anhand eines praktischen Beispiels einmal durchzuführen, wird das Standardbeispiel für diesen Abschnitt leicht verändert.
Das Hauptdokument, es heißt in unserem Beispiel frameset.html, hat folgenden Aufbau:
<html>
<head><title>Frames</title></head>
<frameset cols="150,*">
<frame name="A" src="a.html" />
<frame name="B" src="f_b.html" />
</frameset>
<noframes>
<body>Ihr Browser kann mit Frames nichts anfangen!</body>
</noframes>
</html>
Die Datei f_b.html enthält wiederum ein Frameset:
<html>
<head><title>Noch mehr Frames</title></head>
<frameset rows="100,*">
<frame name="C" src="c.html" />
<frame name="D" src="d.html" />
</frameset>
</html>
Hier klicken, um das Bild zu Vergrößern
Abbildung 10.3 Die Beispielseite im Browser
Abbildung 10.3 veranschaulicht noch einmal den Aufbau: Die Hauptseite enthält zwei Frames – links a.html und rechts f_b.html. Zusätzlich teilt sich f_b.html in die zwei Frames mit dem Inhalt c.html (oben) und d.html (unten) auf.
10.2.1 Auf übergeordnete Frames zugreifen
Wenn Sie in HTML codieren wollen (von »programmieren« kann im Zusammenhang mit HTML ja keine Rede sein), dass sich das Ziel eines Links nicht im aktuellen Frame öffnet, sondern das gesamte Browserfenster für sich beansprucht, machen Sie das für gewöhnlich folgendermaßen:
<a href="seite.htm" target="_top">Hier klicken</a>
Bei JavaScript heißt das Schlüsselwort ganz ähnlich: top. Hiermit erhalten Sie eine Referenz auf das oberste Fenster in der gesamten Frame-Hierarchie. Egal, ob Sie sich in a.html, c.html oder gar in frameset.html befinden – top.location enthält immer "frameset.html" (sowie den Pfad zu dieser Datei).
Oft ist es jedoch von Nutzen, in der Hierarchie nur eine Stufe nach oben zu gelangen, also in unserem Beispiel etwa von c.html eine Referenz auf den Frame mit b.html zu erhalten. Das Schlüsselwort heißt hier parent (dt. »Elternteil«). Wenn Sie sich also in c.html befinden, enthält parent.location den Wert "b.html"; und von b.html aus betrachtet, enthält parent.location den Wert "frameset.html".
Mit parent erhält man bildlich betrachtet immer eine Referenz auf das Dokument, das in dem <frameset>-Tag enthalten ist, das den Frame mit der aufrufenden Datei enthält. Sind in einer Datei also mehrere Framesets ineinander verschachtelt, stehen diese dennoch in der Frame-Hierarchie auf einer Ebene.
Einige Seiten, die ihre Navigation mit Frames erledigen, stellen externe Links in einem ihrer Frames dar, so dass die eigene Navigation immer noch sichtbar bleibt, die fremde Website also in einem Unterframe einer anderen Website dargestellt wird. Das ist zum einen unfreundlich und zum anderen auch schon ein paar Mal erfolgreich von entnervten Sitebetreibern an- bzw. abgemahnt worden. Wenn Sie verhindern wollen, dass Ihre Seiten innerhalb eines fremden Framesets dargestellt werden, haben Sie die folgenden Möglichkeiten, die alle auf demselben Prinzip beruhen: Wenn wir uns nicht im obersten Frame in der Hierarchie befinden, dann machen wir uns zum obersten Frame in der Hierarchie!
<script type="text/javascript"><!--
if (self != top) {
top.location = self.location;
}
//--></script>
oder:
<script type="text/javascript"><!--
if (self != parent) {
top.location = self.location;
}
//--></script>
Bauen Sie dieses Skript in all Ihre Webseiten ein, und Sie werden nie wieder von fremden Framesets belästigt werden (wie üblich: sofern JavaScript aktiviert ist)!
Es gibt jedoch auch noch einen anderen Fall: Ein Besucher Ihrer Webseiten setzt ein Lesezeichen (Bookmark) auf eine Seite in einem Frame. Wenn er jedoch das Lesezeichen wieder aufruft, wird lediglich die eine Seite geladen, nicht jedoch die anderen Frames, die beispielsweise die gesamte Navigation enthalten.
Um das Ganze ein wenig anschaulicher zu machen, folgt hier eine Konkretisierung der Aufgabenstellung. Ihre Website besteht aus zwei Frames: Der linke enthält die Navigation, der rechte den Inhalt. Ihre index.html-Datei sieht folgendermaßen aus:
<html>
<head><title>Homepage mit Frames</title></head>
<frameset cols="150,*">
<frame src="navigation.html" name="Navi" />
<frame src="inhalt.html" name="Inhalt" />
</frameset>
<noframes>
<body>Ihr Browser kann mit Frames nichts anfangen!
</body>
</noframes>
</html>
In allen Unterseiten sollte nun überprüft werden, ob ein Frameset existiert; wenn nicht, soll das Frameset aufgebaut werden. Dies geschieht mit ein wenig JavaScript: Die URL der aktuellen Seite wird über die Kommandozeile an eine Datei frameset-laden.html übergeben, die die Frame-Struktur erzeugt.
Der folgende Code gehört in alle Unterseiten:
<script type="text/javascript"><!--
if (self == top) {
location.href = "frameset-laden.html?" + location.href;
}
//--></script>
Die Datei frameset-laden.html wertet die Kommandozeile aus und baut – mittels document.write() – die Frame-Struktur auf. Beachten Sie, dass Sie das gesamte Frameset mit JavaScript erzeugen müssen; eine Mischung ist nicht möglich (sie wäre zwar syntaktisch prinzipiell korrekt, die Browser kommen damit aber nicht zurecht).
<html>
<head><title>Homepage mit Frames</title></head>
<script type="text/javascript"><!--
function tag(s) {
document.write("<" + s + ">");
}
tag('frameset cols="150,*"');
tag('frame src="navigation.html" name="Navi"');
seite = location.search.substring(1,
location.search.length);
//location.search beginnt mit "?" !
tag("frame src='" + seite + "' name='Inhalt'");
tag("/frameset");
//--></script>
</html>
Denken Sie daran, dass location.search mit einem Fragezeichen beginnt; Sie dürfen daher die Zeichenkette erst ab dem zweiten Zeichen (es hat den Index 1) auswerten.
Die mit JavaScript generierten Framesets lassen sich noch weiter verfeinern. Wenn Sie eine größere Website haben, wird sich im (fiktiven) linken Frame nicht immer dieselbe Navigation befinden. Sie können mehrere Frameset-Dateien erstellen und – je nach aufgerufener Unterseite – einen Link auf eine bestimmte dieser Frameset-Seiten setzen. Stellen Sie sich folgende Zuordnungstabelle vor (Tabelle 8.1):
Tabelle 10.1 URLs und zugehörige Frameset-Dateien
URL-Format
|
Zugehöriges Frameset
|
/produkte/...
|
|
/investorrelations/...
|
|
Sonstige
|
|
Der Code könnte hier folgendermaßen aussehen:
<script type="text/javascript"><!--
if (self == top) {
if (location.href.indexOf("/produkte")>=0) {
location.href = "frameset1.htm?" + location.href;
} else if (location.href.indexOf("/investorrelations")>=0) {
location.href = "frameset2.htm?" + location.href;
} else {
location.href = "frameset3.htm?" + location.href;
}
}
//--></script>
Bei einem Schreibzugriff auf location.href wird sofort die neue Seite geladen; JavaScript-Code, der dahinter folgt, wird nicht mehr ausgeführt. Sauberer wäre es natürlich, mit else zu arbeiten.
10.2.2 Auf Daten von Unterframes zugreifen
Referenzen auf die Unterframes eines Fensters werden in dem Array frames (um genau zu sein, unter window.frames oder self.frames oder this.frames) gespeichert. Wenn Sie in der Datei frameset.html auf den linken Frame zugreifen wollen, haben Sie folgende Möglichkeiten:
|
window.A |
|
window.frames[0] |
|
window.frames["A"] |
Aber immer der Reihe nach:
|
window.A: A ist der Wert des name-Attributs des entsprechenden Frames. Zwei Dinge sollten Ihnen hier deutlich werden: Geben Sie jedem Frame einen einprägsamen Namen, und verwenden Sie keine JavaScript-Begriffe (beispielsweise sollten Sie den Frame nicht "location" nennen, Sie können dann nicht mit window.location auf ihn zugreifen). |
|
window.frames[0]: Wie bereits erwähnt, werden Referenzen auf alle Frames in der jeweiligen HTML-Seite in dem Array frames abgespeichert. Wie bei allen JavaScript-Arrays beginnt der Index bei 0; frames[0] bezeichnet also den ersten Frame. Die Frames werden dabei nach dem Vorkommen im HTML-Dokument nummeriert. Mit frames[1] erhält man somit eine Referenz auf den zweiten Frame in der Beispielseite (b.html). |
|
window.frames["A"]: Das ist eine Mischung aus beiden Varianten. Hier kann man auch auf Frames mit speziellen Namen wie etwa "location" zugreifen. Diese Methode bedeutet jedoch recht viel Tipparbeit, und Sie werden sie sehr selten sehen. Je nachdem, wie einprägsam Ihre Frame-Namen sind, sollten Sie eine der beiden ersten Methoden verwenden. |
Abbildung 10.4 verdeutlicht die Zugriffsmöglichkeiten auf andere Frames noch einmal ganz allgemein.
Hier klicken, um das Bild zu Vergrößern
Abbildung 10.4 Zugriffsmöglichkeiten auf andere Frames
Mit diesem Wissen können Sie fremden Frames auch noch auf die folgenden Weisen entkommen. Denken Sie daran: Wenn Sie sich im obersten Frame in der Hierarchie befinden, zeigen parent und top auf das aktuelle Fenster:
<script type="text/javascript"><!--
if (top.frames.length>0) {
top.location = self.location;
}
//--></script>
oder:
<script type="text/javascript"><!--
if (parent.frames.length>0) {
top.location = self.location;
}
//--></script>
Sie sehen hieran schon, wie man die beiden Methoden – top und parent bzw. frames – miteinander kombinieren kann: Will man auf einen Frame zugreifen, der sich auf derselben Hierarchie-Ebene befindet, so greift man mit top oder (ggfs. mit mehrfacher Anwendung von) parent auf den nächsten gemeinsamen Vorfahren der beiden Frames zu und geht dann über frames in der Hierarchie wieder hinab. Um im Beispiel von oben von dem Frame mit der Datei c.html zu dem Frame mit der Datei d.html zu gelangen, gibt es folgende Möglichkeiten:
|
parent.D |
|
top.B.frames[1] |
|
parent.parent.B.D |
Wenn Sie Zugriff auf den Frame haben, können Sie damit auch auf alle Eigenschaften des Frames oder seiner Unterobjekte zugreifen, beispielsweise auf document, location oder auch auf globale Variablen im JavaScript-Code des Frames.
10.2.3 Mehrere Frames gleichzeitig ändern
Eine der Fragen, die in Newsgroups am häufigsten gestellt werden, lautet: »Wie kann ich den Inhalt mehrerer Frames gleichzeitig ändern?« Dies ist eine Problemstellung, die tatsächlich häufig vorkommt. Denken Sie an eine der Standardanwendungen für Frames: die Navigation. Wenn Sie einen Punkt in der Navigation auswählen, wird (in einem anderen Frame) die entsprechende Inhaltsseite geladen. Jedoch kann es auch vorkommen, dass sich das Aussehen der Navigation selbst ändern soll (beispielsweise soll der gerade ausgewählte Navigationspunkt hervorgehoben werden, damit der Benutzer weiß, wo er sich befindet).
Dieser Effekt ist sehr einfach umzusetzen. Auf die oben aufgezeigte Art und Weise wird auf die entsprechenden Frames zugegriffen und die Eigenschaft location.href entsprechend verändert. Hier lohnt es sich, eine eigene Funktion zu schreiben, die Sie wiederverwenden können. Als Parameter werden Referenzen auf die Frames sowie die zu ladenden URLs übergeben:
<script type="text/javascript"><!--
function ZweiFrames(frame1, url1, frame2, url2) {
frame1.location.href = url1;
frame2.location.href = url2;
}
//--></script>
Stellen Sie sich vor, im vorherigen Beispiel befinden Sie sich im Frame A und wollen über einen Link in Frame C die Seite cc.html sowie in Frame D die Seite dd.html laden. Sie können das folgendermaßen tun:
<a href="javascript:ZweiFrames(parent.B.C, 'cc.html', parent.B.D, 'dd.html');">hier klicken</a>
Sie sollten jedoch immer eine Alternative zur Verfügung haben, wenn der Browser des Benutzers kein JavaScript unterstützt. In diesem Fall ist das relativ einfach: Wenn Sie sich die Beispielseite noch einmal anschauen, sehen Sie, dass die Frames C und D in der Datei b.html definiert werden. Sie könnten also alternativ eine Datei bb.html erstellen, die folgendermaßen aussieht:
<html>
<head><title>Noch mehr Frames</title></head>
<frameset rows="100,*">
<frame name="C" src="cc.html" />
<frame name="D" src="dd.html" />
</frameset>
</html>
Der Link vereinfacht sich dann zu folgendem puren HTML:
<a href="bb.html" target="B">hier klicken</a>
Überlegen Sie also immer, ob Sie die Aufgabenstellung auch ohne JavaScript lösen können. Sie vergrößern damit das mögliche Publikum für Ihre Webseite.
10.2.4 JavaScript in Frames auslagern
Wenn Sie eine größere Website mit Frames aufgebaut haben und recht viel JavaScript verwenden, lohnt es sich natürlich, oft benutzte Befehle in Funktionen auszulagern und diese Funktionen auf irgendeine Seite in einem der Frames zu schreiben. Mittels top, parent und frames kann dann auf diese Funktion zugegriffen werden. Ein nahe liegender Ansatz ist es, die Funktionen in der obersten Seite der Hierarchie, also der Seite mit dem Haupt-Frameset abzulegen. Die Funktionen können dann mit top.funktionsname() aufgerufen werden.
Am besten ist es jedoch, wenn alle JavaScript-Funktionen und globalen Variablen in einem Frame abgelegt werden; idealerweise in einem Frame, der sich nie ändert (beispielsweise in einem Navigationsframe). Alternativ dazu können Sie auch auf einen alten HTML-Trick zurückgreifen und einen unsichtbaren Frame verwenden. Der folgende Code erzeugt zwei Frames, wobei der zweite jedoch nicht sichtbar ist, da er nur einen Pixel hoch ist. In diesem Frame lassen sich bequem Funktionen und Variablen ablegen.
<html>
<head><title>Unsichtbarer Frame</title></head>
<frameset rows="*,1" border="0" cellspacing="0" frameborder="0">
<frame name="sichtbar" src="inhalt.htm" />
<frame name="unsichtbar" src="skript.htm" />
</frameset>
<noframes>
<body>Ihr Browser kann mit Frames nichts anfangen!
</body>
</noframes>
</html>
10.2.5 Frames zählen
Abschließend noch eine praktische Hilfsfunktion, die erneut den Einsatz von Frames zeigt und auch Programmierung mit Rekursion. Die Funktion subframes(frameref) soll die Anzahl der Unterframes des Frames mit der Referenz frameref ermitteln. Als Unterframes zählen hierbei jedoch nur die Frames, die ein Dokument ohne weiteres Frameset enthalten. Das geht sehr einfach mit Rekursion: Hat ein Frame keine Unterframes, wird er als Dokument gezählt; andernfalls wird die Funktion subframes() rekursiv für alle Unterframes aufgerufen:
function subframes(frameref) {
if (frameref.frames.length==0) {
return 1;
} else {
var summe = 0;
for (var i=0; i<frameref.frames.length; i++) {
summe += subframes(frameref.frames[i]);
}
return summe;
}
}
|