Multimediakurs Leinfelder: Modul 11 - Anlagen: Forced Frames und Vermeidung von Webnapping

(© copyright)     Letzte Änderungen:01.11.2010


Position innerhalb des Kurses

Modul 11: Anlage 13: Immer-mit-Frames und Vermeidung von Webnapping

von Reinhold Leinfelder [an error occurred while processing this directive]

Die Verwendung von Frames ist nach wie vor eine Glaubensfrage, wir gingen im Kurs näher auf die Pros und Cons deren Verwendung ein (z.B. Modul 4.5). Auch der Kursleiter verwendet bei vielen Seiten Frames (z.B. diese Kursseiten), bei anderen jedoch nicht (z.B. Unterseiten von http://www.palaeo.de/palges). So oder so, Frames gibt es im Web zuhauf, und damit sollten Sie als Webproduzent mit den Problemen umgehen können.

Aufgrund mehrerer entsprechender Anfragen reiche ich diese Anlage als Ergänzung zu den Frames-Modulen sowie des JavaScript-Moduls nach.

Problem 1: Sie verwenden bewusst keine Frames, wollen aber verhindern, dass Ihre Seiten (übrigens verbotenerweise, sofern nicht abgesprochen) innerhalb von Framesets eines anderen Anbieters geladen werden (sog. Webnapping, sehen Sie zu den rechtlichen Aspekten auch unsere Anlage 7)

Problem 2: Ihre Frameseiten werden z.B. bei der Verwendung von Suchmaschinen isoliert (d.h. außerhalb des für den Kontext wichtigen Framesets) aufgerufen. Oder aber Sie wollen Links zu speziellen Kapiteln innerhalb Ihrer Frameset-Seiten weitergeben. Wie kann man sicherstellen, dass die Seiten auch im zugehörigen Frameset erscheinen?


Problemlösung 1: Seite soll nie in einem Frameset aufgerufen werden können:

Mit einem Mini-Javascript haben wir dies gleich erreicht:

Kopieren Sie folgendes Skript in den Header der entsprechenden Seite:

<script language="JavaScript">
function nixframe() {
if (self.parent.frames.length != 0)
self.parent.location = document.location;
}
</script>

Dieses Skript rufen Sie im Body-Tag mit dem Onload-Eventhandler auf.

<body bgcolor="#ffffff" onload="nixframe()">

Das war's. Von nun an kann keiner diese Seite via bewusstem oder versehentlichem Webnapping in Framesets einbinden.

Erläuterung: Es wird darauf getestet, ob mehr als ein Frame vorhanden ist (frame.length !=0) (bei JavaScriptzählungen bedeutet 0 die Zahl 1), wenn allso !=0 erfüllt ist (verständlich ausgedrückt: mehr als ein Frame vorhanden ist), wird das Dokument einfach nochmals geladen, allerdings mit dem Objekt und Unterobjekt self.parent, was alle Frames bricht (entspricht dem html-Target =_self).

Ein Beispiel finden Sie weiter unten. Rufen Sie dieses auf (Frameset) und dann den Eintrag Frameweg. Obwohl mit target="hauptfenster versucht wird, diese Datei ins Hauptfenster des Framesets zu laden, wird das Set aufgrund des obigen Skriptes gebrochen.


Problemlösung 2: Seite soll immer in Frameset erscheinen. Hier gibt es mehrere Lösungsansätze, insgesamt ist dies deutlich aufwändiger als die Behebung von Problem 1:

Möglichkeit a) Klassischer (und technisch sicherer) Weg: Geben Sie am Ende oder am Anfang jeweils eine Referenz mit Link zur zugehörigen Frameset-Seite. Verwenden Sie dafür einen absoluten Link sowie das Target-Attribut _top.Target = "_top" bricht alle evtl. ansonsten vorhandenen Frames und beginnt im leeren Browserhauptfenster.

Die Seiten dieses Multimediakurses verwenden diese Möglichkeit (s.o.). Der jeweilige Link zur Eingangsseite lautet in unserem Fall im code: <a href="http://www.palaeo.de/multimediakurs" target="_top">Multimediakurs Leinfelder</a>

Leider gibt es keine technische Möglichkeit, ohne die Verwendung von Skriptsprachen durch einen Link sowohl den Eingangsframeset zu laden als auch eine spezielle Seite in einem der Frames anzuzielen (sofern es sich nicht um eine im Eingangsframeset definierte Frameseite handelt).

Möglichkeit b) Komplexe html-Lösung ohne Verwendung von Skripten

Sie erstellen für alle wesentlichen Seiten Ihres Angebots einen extra Frameset, indem Sie Ihren Eingangsframe kopieren, umbenennen (z.B. indexkap1.html, indexkap2.html), aber die Framenamen gleich belassen. Sie verbinden dann in in Ihren Hauptframe jeweils die gewünschte Seite (z.B. in einem Frame namens 'hauptframe' der Seite indexkap1.html die Seite kap1.html etc.).

Diese Lösung erlaubt die Weitergabe von Links zu Unterkategorien (z.B. www.xyz.de/meinesupersite/indexkap1.html), für die auch Bookmarks angelegt werden können. Jedoch müssen Sie ganz genau auf die korrekte Verwendung von targets achten (_top, _parent, framenamen usw.), um nicht Ihre eigenen Framesets versehentlich unterzuverschachteln. Mit dieser Lösung kann man sehr schnell in Teufels Küche komme. Etwas für html-Puristen, die nicht auf Frames, aber auch nicht auf direkte Ansteuerung verzichten wollen. Meines Erachtens nicht empfehlenswert (obwohl bzw. gerade weil ich selbst bereits entsprechende Sites erstellt habe). Den isolierten Aufruf von Seiten außerhalb von Frames aus Suchergebnissen von Webrobots heraus verhindert auch diese Lösung nicht.

Möglichkeit c) Verwendung von JavaScript

Im Web finden sich verschiedene JavaScript-Möglichkeiten, die Forced Frames (d.h. das Erzwingen von Framesets) ermöglichen sollen. Je nach verwendetem Browser ergeben Sie jedoch häufig Fehlermeldungen und verhindern z.T. sogar den Aufruf des Seite, sehr unschön.

Der Webeditor GoLive 5 kommt z.B. mit einer sog. vorgefertigten Aktion (force frames), die aber (zumindest in meiner Version) nur bewirkt, dass das jeweilige Eingangsframeset mit den dort definierten Seiten aufgerufen wird (es sei denn, man gibt dort von Hand noch ein weiteres Skript ein). Ein interessierter online-Kursnutzer lies mir jedoch einen Link zu einem Skript zukommen, welches (mit ein paar kleinen Anpassungen) auf allen von mir getesteten Browsern funktioniert.

Probieren Sie unser Beispiel 1 erst einmal aus:

Hier ist unser Frameset (die Datei index.html definiert das Frameset und wird in einem Fenster namens 'test' geöffnet; eingeladen werden titel.html, menu.html und intro.html).

Wichtig: diese Anlage macht Sie nicht mit der Frame-Syntax vertraut, dies setze ich als bekannt voraus. Arbeiten Sie ggf. Modul 3 durch, um sich mit Frames vertraut zu machen.

Probieren Sie mit Rechtsklick (bzw. Control-Click beim Mac) auch mal, die im Hauptframe geladenen Seiten in ein neues Fenster zu laden, um den Effekt des Skripts zu sehen. Probieren Sie dies auch mal beim Menu (linker Frame) und Titel (Topframe) aus.

Alternativ können Sie die einzelnen Seiten hier dierekt aufrufen:

intro.html | kap1.html | kap2.html | kap3.html | impressum.html | menu.html | titel.html

Die Skripte:

Wir benötigen zwei verschiedene Skripte, eines für die jeweiligen Einzelseiten sowie ein weiteres für die Frameset-Seite (in unserem Beispiel also die Index-Seite).

Hinweis: Diese Anlage macht Sie nicht mit der Syntax von JavaScript vertraut. Sie sollten wenigstens Modul 7.1 unseres JavaScript-Kurses durchgesehen haben. Wenn Sie die Sytax verstehen wollen, sollten Sie den gesamten Modul 7 durchgearbeitet haben, dies ist jedoch zur Verwendung der Skripte nicht notwendig.

Beim Skript für die jeweiligen Einzelseiten habe ich nur eine kleine Änderung ggf. dem Skript von M. Wernecke gemacht.

Skript 1

<html>
<head>
<title>mein Titel</title>

<script language="JavaScript">
<!--
function checkFrameset() {
if(!parent.topframe)
location.href="index.html?" + location.pathname;
}
//-->
</script>

</head>

Obiges Skript kommt in den Header-Teil der Seiten (einen Minimal-Header habe ich mit angegeben).

Die Funktion checkFrameset() wird beim Laden der Seite mit dem onload-Attribut im body-Tag aufgerufen:

<body bgcolor="#ffffff" onload="checkFrameset()">

Die Originalerklärung können Sie auf der o.a. Seite von selfhtml nachlesen; weiter unten finden Sie die Erklärung des hier verwendeten, angepassten Skriptes. Ich verwende zur Vereinfachung und zur Vermeidung mancher Fehler (s.u.) jedoch statt der Angabe eines absoluten Pfades nur die Dateinamen.

Wichtig:

In unserem Beispiel liegen alle o.a. Dateien im selben Ordner. Alle o.a. Dateien (mit Ausnahme von index.html) verwenden dieses Skript.

Erläuterung: das Skript prüft zuerst, ob es einen Frame mit Namen topframe gibt, genauer, ob es diesen nicht gibt (deshalb der Operator ! vor dem Ausdruck; analog ginge genauso if(parent.topframe==null), was Ihnen vielleicht etwas logischer vorkommt. Wenn die Bedingung nicht erfüllt ist (also ein entsprechender Frame namens topframe vorhanden ist), passiert gar nichts (da die Seite dann sicherlich in ihrem Frameset geladen wurde). Anderfalls (falls also kein derartig benannter Frame vorhanden ist) wird die Index-Seite (also die Frameset-Seite) via location.href-Eigenschaft neu aufgerufen. Allerdings wird nicht nur index.html an den Browser übergeben, sondern dies ergänzt um ?Pfadnamen der aktuellen Seite. Was da an den Browser übergeben wird, könnte also z.B. sein: index.html?/immerframes/kap1.html

Damit kann der Browser aber erst mal nichts anfangen. Er lädt index.html neu und ignoriert alles, was dahinter steht. Damit ist aber nicht kap1.html wie gewünscht im Hauptframe geladen, sondern die Seite, die im Frameset definiert wurde (in unserem Beispiel wäre dies intro.html).

Aus diesem Grunde brauchen wir auch auf der Frameset-Seite (in unserem Beispiel also auf der index.html-Seite) noch ein Skript (Skript 2), welches diese ankommenden Angaben (eigentlich ein sog. Suchstring, da mit ? eingeleitet) weiter verarbeitet.

Im Header der Frameset-Seite muss als Minimallösung folgendes Skript stehen:

Script 2:

<script language="JavaScript">
<!--
function checkFramecall() {
var Adressanhang=location.search;
if(Adressanhang)
frames.hauptframe.location.href=Adressanhang.substring(1, Adressanhang.length);
}
//-->
</script>

Dieses Skript muss aber auch noch aufgerufen werden. Da es sich um eine Frameset-Seite handelt, muss das Skript möglichst sofort aufgerufen werden, bevor die Frame-Seiten geladen werden. Eine Angabe im Body-Tag ist deshalb zu spät (auch, weil der Body-Inhalt bei einer Frameset-Seite ja nur abgearbeitet wird, falls der Browser keine Frames darstellen kann). Wir packen den onload-Aufruf deshalb gleich in die Definition des ersten Framesets:

<frameset rows="96,*" onload="checkFramecall()">
.........

Obiges Skript für die Frameset-Seite entspricht dem Originalskript von M. Wernecke. Was passiert im Einzelnen? Es wird zuerst eine Variable Adressenanhang (Name beliebig) definiert. Diese bekommt als Wert den überlieferten Suchstring (location.search), also alles, was der Browser ab dem Fragezeichen übermittelt hat.

Die if-Abfrage prüft dann, ob die variable Adressanhang überhaupt einen Wert hat -Sie könnten auch schreiben if(Adressenanhang!=null) . Der Suchstring wurde ja nur übermittelt, wenn sich die aufrufende Seite (bleiben wir mal bei kap1.html) ja nicht in unserem Frameset befand.

Wenn also der Suchstring übermittelt wurde, muss von diesem noch das Fragezeichen rausgeschnitten werden, dann haben wir den korrekten Pfad samt Dateinamen und können ihn in den gewünschten Frame, nämlich in unserem Beispiel mit Namen "hauptframe" laden. Erläutern wir es lieber am Beispiel:

Unser übermittelter Suchstring sei (vgl. oben):

?/immerframes/kap1.html

Mit der Methode substring() wird von unserer Variablen Adressenanhang, d.h. vom gerade genannten Suchstring nur ein Teil herausgeschnitten. In substring wird jeweils das erste herauszuschneidende Zeichen sowie das letzte herauszuschneidende Zeichen definiert. Adressanhang.substring(2,6) würde als erstes Zeichen das dritte Zeichen stehenlassen (für JavaScript beginnen Zählungen mit 0, was dem ersten Zeichen entspräche), nach dem 7. Zeichen (in JavaScript: 6) würde dann ebenfalls alles gelöscht. Übrig bliebe von unserem Suchstring: immer . Wir wollen aber nur das Fragezeichen löschen, d.h., Zeichen 0 muss gelöscht werden, unser Substring beginnt bei Zeichen 1. Schwieriger schon das Ende. Wir könnten nun Pfadlänge und Dateinamenlänge jeweils abzählen und die korrekte Zahl eintragen. Eleganter ist es, sich die Zeichenlänge des Suchstrings bestimmen zu lassen (mit Adressanhang.length). Diese Zahl können wir direkt verwenden. Unser Suchstring besteht aus 23 Zeichen, d.h. reicht in JavaScript-Notierung bis 22. Genau diesen Wert ergibt auch Adressanhang.length in unserem Beispiel. Die Anweisung frames.hauptframe.location.href=Adressanhang.substring(1, Adressanhang.length); ruft also genau Pfad/Seite immerframes/kap1.html in den zuvor bereits geladenen Frameset index.html

Erweiterungen und Ergänzungen:

Forced frame auch für Titel und Menu-Frames: De facto verwende ich in unserem Beispiel nicht genau obiges Skript, sondern ein von mir erweitertes Skript. Suchmaschinen finden nämlich gerne auch die Titel- oder Menuframes (da hier viele aussagekräftige Begriffe stehen) und rufen diese dann isoliert auf. Besonders unschön. Um zu erreichen, dass diese Seiten ebenfalls gleich im Frameset erscheinen, sind noch ein paar Kniffe notwendig. Würden wir das Detailseitenskript (also z.B. von kap1.html) einfach auf die Seiten titel.html und menu.html kopieren, würden diese, sofern außerhalb des Framesets aufgerufen, zwar in den Frameset geladen, jedoch im falschen Frame erscheinen. Sie sollen ja nicht im Frame namens "hauptframe" geladen werden.

Wir können dennoch dieses Skript für die entsprechenden beiden Seiten verwenden, müssen jedoch dann auf unserer Frameset-Seite das Skript ergänzen. Tatsächlich sieht das von mir verwendete Header-Skript im Beispiel folgendermaßen aus:

<script language="JavaScript">
<!--
function checkFramecall() {
var Adressanhang=location.search;
if (Adressanhang.indexOf("titel")>-1 || Adressanhang.indexOf("menu")>-1)
location.href="index.html";
if(Adressanhang)
frames.hauptframe.location.href=Adressanhang.substring(1, Adressanhang.length);
}

//-->

</script>

Das ganze wird wiederum wie vorhin durch einen onload-Handler im Frameset aufgerufen.

Was geschieht nun hier?.

Der Suchstring (also die Variable Adressanhang) wird zuerst danach durchsucht, ob darin das Wort titel oder menu auftaucht. Unsere beiden Seiten heißen ja titel.html bzw. menu.html. Ein entsprechen übermittelter Suchstring ?/immerframes/menu.html beinhaltet eben ggf. eines dieser Wörter (zum Wiederholen: die logische Verknüpfung in der Suchabfrage ist oder, d.h. ||, gesucht wird mit der Methode indexOf(). Wird eine Übereinstimmung gefunden, ergibt sich ein Wert >-1 (nur falls nichts gefunden wird, gäbe es den Wert = -1).

Wird eines der beiden Wörter gefunden, gibt es eine ganz einfache JavaScript-Redirect-Umleitung zur Frameset-Eingangsseite mittels location.href="index.html";

Falls dies nicht zutrifft, wird weiter abgearbeitet und zwar das Skript wie wir es bereits kennen (s.o.).

Wichtig: Wenn Ihre Titel oder Menuseiten anders heißen, müssen Sie mit der indexOf();-Methode eben nach anderen Schlüsselwörtern suchen lassen.

Forced Frames durch Benutzeraufruf

Vielleicht wollen Sie den Benutzer etwas dezenter darauf hinweisen, dass die Seite eigentlich in einen Frameset gehört und ihn nicht mit dem Reload verwirren. Unser Beispiel Kap.2 zeigt dies. Erst bei Klick auf den entsprechenden Link wird das Skript ausgelöst und das Frameset drumherum geladen.

Wir können natürlich wieder Skript 1 verwenden. Der einzige Unterschied: statt des onload-Eventhandlers im Body verwenden wir einen onclick-Eventhandler für den Link bzw. einen Javascript-Linkaufruf:

Also entweder: <a href="#" onclick="checkFrameset()">in Frameset aufrufen</a>

oder: <a href="javascript:checkFrameset()">in Frameset aufrufen</a>

Das ist prinzipiell schon alles. Wenn allerdings die Seite richtig im Frameset geladen ist, ist der Link ja immer noch da und es passiert nichts, wenn man draufklickt. Ich hab deshalb noch einen kleinen Hinweis eingegeben via if/else-Abfrage angegeben, der besagt, dass die Seite korrekt geladen ist. Die Variation von Script 1 sieht dann so aus:

Script 1b:

<script language="JavaScript">
<!--
function checkFrameset(){
if(!parent.topframe)
{
location.href="index.html?" + location.pathname;}
else
{alert("die Seite ist bereits im Frameset");}
}
//-->
</script>

Tipp für JavaScript-Freaks: Wenn Sie es ganz raffiniert haben wollen, lassen Sie den Link überhaupt nur erscheinen, wenn die Seite außerhalb des Frameset geladen ist. Wie? Nun mit document.write()-Methoden und if-Abfragen. Allerdings nicht ganz trivial, da Sie dann inline-Skripte verwenden müssen, damit die document.write-Methode an der korrekten Stelle funktioniert. Sie können ja mal rumbasteln.

Troubleshooting: Probleme mit Frames in Frames und sonstige Fehlermöglichkeiten.

Wenn obige Skripte auf Ihrer Webseite nicht funktionieren, auch wenn Sie einen Redirect-Vertrag von einem Provider auf einen anderen Server haben, oder wenn Sie meinen, dass obige Skripte etwas"salopp" geschrieben sind, sollten Sie die Troubleshooting-Seite (Anlage 13b) zu diesem Thema durchlesen.

Weitere Möglichkeiten für benutzerkontrollierten Aufruf des ForceFrame-Skriptes mit Hinweisen (Anlage 13c)

© R. Leinfelder und Paläontologie München, letzte Änderung 01.11.2010


Inhaltsseite

Anlagenverzeichnis