JavaScript/Tutorials/Fenster- und Frameszugriff

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

In Javascript gibt es das window-Objekt als oberste Instanz über allen anderen Objekten. Es bietet die Methode window.open() zum Öffnen neuer Fenster an (und außerdem alle weiteren Unterobjekte, Eigenschaften und Methoden, die in der Javascript-Objektreferenz aufgelistet sind).[1]

In JavaScript kannst du mit verschiedenen Browserfenstern, Tabs und Frames (wie iframes) arbeiten, indem du das Window-Objekt verwendest. Dies ist nützlich, um Folgendes zu tun:

  • Popup-Fenster öffnen und mit ihnen kommunizieren
  • andere Webseiten, Widgets oder aktive Inhalte wie Karten, Videos, etc. in einem iframe einbetten
  • Nachrichten zwischen Fenstern oder Frames weitergeben

Dies ist auch heute (2025) noch möglich, aber durch moderne Sicherheitsmechanismen wie Cross-Origin Restrictions (CORS, Same-Origin-Policy), Popup-Blocker und striktere Browserrichtlinien eingeschränkt.


Popover im neuen Fenster

Fenster in JavaScript sind hierarchisch aufgebaut – sie bilden eine Baumstruktur. Jedes Fenster kennt dabei bestimmte verwandte Fensterobjekte:

  • top verweist auf das oberste Fenster der Hierarchie. Ist man bereits ganz oben, zeigt top auf sich selbst.
  • parent zeigt auf das direkt übergeordnete Fenster – oder auf sich selbst, wenn es kein übergeordnetes gibt.
  • self ist das aktuelle Fenster (und kann meist weggelassen werden).
  • opener verweist auf das neue Fenster, das mit window.open() geöffnet wurde – oder ist null, wenn es kein solches gibt.

Mit diesen Eigenschaften kann man gezielt innerhalb verschachtelter Fenster oder eingebetteter Inhalte navigieren.

Ein neues Fenster

Mit Window.open() ist es möglich ein neues „Fenster“ zu öffnen. Dabei haben sich in den letzten Jahren einige Änderungen ergeben: Browser öffnen neue Fenster heute im allgemeinen nicht mehr in einem neuen Fenster oberhalb des aktuellen Fensters, sondern in einer neuen Registerkarte - einem Tab.

Ein Spaß der 90er, einem unbedarftem Nutzer eine Endlosschleife sich selbst öffnender Fenster zu schicken, ist heute so unmöglich:

setTimeout(() => {
  window.open("seite.html"); 
}, 1000);

Browser wie Chrome, Firefox oder Safari blockieren window.open() wenn es nicht direkt durch eine Benutzeraktion ausgelöst wurde (z. B. durch ein Click-Event).

Ein Klick öffnet neues Fenster ansehen …
function fensterOeffnen() { 
  window.open('https://forum.selfhtml.org/');
}

Durch einen Klick auf den Button wird die Funktion fensterOeffnen() aufgerufen, die mit window.open() ein neues Fenster in einer neuen Registerkarte öffnet.


Beachte: In heutigen Browsern wird ein neues Fenster nur noch dann geöffnet, wenn es durch eine Benutzereingabe aktiv eingeleitet wurde.
Im Normalfall wird nur eine neue Registerkarte geöffnet.

iframes - „Fenster im Fenster“?

Iframe-Fensterhierarchie.svg

Ein iframe ist wie ein kleines Fenster innerhalb einer Webseite.

  • Es zeigt eine eigene Webseite innerhalb einer anderen Webseite.
  • Es hat einen eigenen DOM-Baum, eine eigene window-Instanz und einen eigenen JavaScript-Kontext.

Dadurch wirkt es tatsächlich wie ein „eingebettetes Fenster“ im Browserfenster.

Allerdings ist ein echtes Browserfenster (z. B. durch window.open()) unabhängig vom Hauptfenster.

Auch vom Sicherheitsmodell (Same-Origin-Policy) wird ein iframe strenger behandelt als z. B. ein selbst geöffnetes Fenster.

iframes passend dimensionieren

Im SELF-Forum wird häufig gefragt, wie man die Dimensionen der eingebundenen Inhalte ermitteln kann, um dann den iframe entsprechend anzupassen, damit alle Inhalte angezeigt werden und keine Scrollbalken erscheinen.
(Die Default-Stylesheets verwenden 300 x 150px!)

Zuerst schaffen wir eine Demo-Seite mit wechselnder Höhe:

iframe mit wechselnder Höhe ansehen …
function changeHeight() {
  const content = document.getElementById("content");
  const output = document.getElementById("output");

  const newHeight = rand(1,20); 

  content.style.height = newHeight + "em";
  output.textContent = "Höhe: " + newHeight + "em";
}

Dem HTML-Element mit der id content wird über eine Zufallsfunktion rand(min, max) eine wechselnde Höhe newHeight zugewiesen. Im div befindet sich ein output-Element, dass die jeweilige Höhe anzeigt.

Diese Webseite wird nun in unser Beispiel eingebunden:

iframe mit dynamischem Inhalt ansehen …
<iframe 
  id="myFrame" 
  src="Iframe-embedded-1.html">
</iframe>

Mit JavaScript wird nun das eingebettete Elemente aufgerufen:

ResizeObersver is watching … ansehen …
    const iframe = document.getElementById('myFrame');

    iframe.addEventListener('load', () => {
      const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
      const iframeBody = iframeDocument.body;

      const observer = new ResizeObserver(entries => {
        for (const entry of entries) {
          const height = entry.contentRect.height;
          iframe.style.height = (height + 36) + 'px';
        }
      });

      observer.observe(iframeBody);
    });

Der iframe wird über seine id identifiziert und ein EventListener angehängt, der das load-Event überwacht.

Sobald dies erfolgt ist, wird mit einem ResizeObserver auf Höhenänderungen reagiert, indem die Höhe des iframes über style.height angepasst wird.

Beachte: Diese Vorgehensweise funktioniert nur, wenn die eingebettete Datei vom gleichen Ursprung stammt (gleiche Domain, gleicher Port und gleiches Protokoll).

Von Fenster zu Fenster reden

Wenn die untergeordnete Seite des iframe nicht denselben Ursprung hat (d. h. andere Domain, Subdomain, Port oder Protokoll), dann kann die Elternseite aufgrund der Same-Origin-Policy des Browsers nicht direkt auf das DOM des Kinds zugreifen.

In diesem Fall muss das Kind die Höhe bereits ermitteln und mittels postMessage an die Elternseite melden.

postMessage ansehen …
const observer = new ResizeObserver(entries => {
  for (const entry of entries) {
    const height = Math.round(entry.contentRect.height);
    window.parent.postMessage(
      { type: 'resize', height: height },
      "https://wiki.selfhtml.org/"
    );
  }
});

Die Child-Seite erhält wieder einen ResizeObserver, der die Höhe ermittelt und sie dann an window.parent sendet.

postMessage hat zwei Parameter:

  • { type: 'resize', height: height }: Die zu übertragenden Daten.
  • der erwartete Origin der Elternseite. Für den Fall, dass der iframe-Inhalt von einer fremdem Seite eingebunden wird, lässt sich so verhindern, dass Daten gestohlen werden.


Kommunikation zwischen Eltern und Kind ansehen …
  const iframe = document.getElementById('myFrame');

  window.addEventListener('message', (event) => {
    if (event.origin !== "https://wiki.selfhtml.org") return;

    const data = event.data;

    if (data?.type === 'resize' && typeof data?.height === 'number') {
      iframe.style.height = (data.height) + 'px'; 
    }
  });

Sicherheit und Kontrolle

Framekiller-Strategien

Verhindern kann man das Einbinden der eigenen Webseite in ein Frameset durch JavaScript, ähnlich wie bei dem Adressierungsproblem. Derartige JavaScripte werden auch als Framekiller oder Framebuster bezeichnet.


Framekiller
<script>
  if (top!=self) top.location.href=self.location.href;
</script>

Alternativ kann der Response Header Content-Security-Policy direkt im Webserver gesetzt werden. Mit der Direktive frame-ancestors lässt sich dabei steuern, ob und von wem eine Webseite in einem <frame> oder <iframe> eingebettet werden darf.

  • Content-Security-Policy: frame-ancestors 'none' keine einbettung
  • Content-Security-Policy: frame-ancestors 'self' nur Webseiten der same origin

Bei Shared Hostern, bei denen kein direkter Zugriff auf die Webserver-Konfiguration möglich ist, kann – sofern Apache als Webserver verwendet wird – der entsprechende Response Header auch über die .htaccess-Datei gesetzt werden, beispielsweise mit:

Header set Content-Security-Policy "frame-ancestors 'none'"

Weblinks

  1. Dieser Artikel ist eine Neubearbeitung eines Selfhtml-aktuell-Artikels von Sven Rautenberg, 29.07.2002
    JavaScript: Fenster- und Frameszugriff –: Zugriff auf komplexe verschachtelte Fensterobjekte

Website mit COOP und COEP „ursprungsübergreifend isoliert“ machen (web.dev)


ToDo (weitere ToDos)

Dies ist der Versuch einer Neukonzeption.

Peer Review und weitere Ideen erbeten.

--Matthias Scharwies (Diskussion) 06:30, 26. Jun. 2025 (CEST)