Wie verständigen sich Dokumente (im iframe) auf einer Webseite oder gar von Tab zu Tab im Browser?
Linuchs
- javascript
- medien
Moin,
mir ist keine direkte Möglichkeit bekannt, wie sich ein Dokument von Server A mit einem Dokument von Server B im iframe des A verständigen kann.
Ich brauche sowas, wenn mein Kalender mit unterschiedlich vielen Terminen in eine fremde Seite eingebettet wird. Entweder ist <iframe> height großzügig bemessen und es bleibt viel Leere übrig oder height ist zu gering und der iframe bekommt einen Scrollbalken.
Habe eine Lösung gefunden, aber sehr kompliziert:
Wenn Dokument A geladen ist, wird mit einer Zeitverzögerung ein Bild vom Server B geholt. Die Höhe des Bildes ist die Höhe des iframe.
Wenn Dokument B geladen ist, meldet es seine Höhe an den eigenen Server B, der legt ein schmales image dieser Höhe als Datei an.
Mache mir zunutze, dass Javascript nicht „fremdgehen“ darf. Ausgenommen fremde Bilder laden.
Nun habe ich irgendwo aufgeschnappt, dass sich Dokumente im Browser mit Nachrichten verständigen können. Ich meine, vor einiger Zeit (jetzt nicht mehr) wurde sogar der Ton bei Youtube ausgeschaltet (oder gar das Video gestoppt), wenn Youtube auch in einem anderen Tab gestartet wurde.
Für meine Liederbücher mit den vielen Liedern, jeweils eins pro iframe, hätte ich gerne so eine Verständigung.
Im iframe kann man pro Lied eine Audio-Datei starten. Wäre schön, wenn die bei Audio-Start im anderen iframe verstummt.
Wie heißt dieses Nachrichten-Konzept und wo kann ich mich einlesen?
Linuchs
Hallo,
mir ist keine direkte Möglichkeit bekannt, wie sich ein Dokument von Server A mit einem Dokument von Server B im iframe des A verständigen kann.
normalerweise gar nicht. Dokument A gibt B einen Ausschnitt mit fest definierter Größe, in dem B sich austoben kann. Eine Interaktion zwischen beiden ist nicht vorgesehen.
Sie ist möglich, wenn Dokument A und B von demselben Server kommen (sprich: Protokoll, Hostname und Port sind identisch). Sonst nicht.
Habe eine Lösung gefunden, aber sehr kompliziert:
Wenn Dokument A geladen ist, wird mit einer Zeitverzögerung ein Bild vom Server B geholt. Die Höhe des Bildes ist die Höhe des iframe.
Wenn Dokument B geladen ist, meldet es seine Höhe an den eigenen Server B, der legt ein schmales image dieser Höhe als Datei an.
Mache mir zunutze, dass Javascript nicht „fremdgehen“ darf. Ausgenommen fremde Bilder laden.
Das hört sich ziemlich wüst an.
Nun habe ich irgendwo aufgeschnappt, dass sich Dokumente im Browser mit Nachrichten verständigen können. Ich meine, vor einiger Zeit (jetzt nicht mehr) wurde sogar der Ton bei Youtube ausgeschaltet (oder gar das Video gestoppt), wenn Youtube auch in einem anderen Tab gestartet wurde.
Tatsächlich??
Für meine Liederbücher mit den vielen Liedern, jeweils eins pro iframe, hätte ich gerne so eine Verständigung.
Im iframe kann man pro Lied eine Audio-Datei starten. Wäre schön, wenn die bei Audio-Start im anderen iframe verstummt.
Wie heißt dieses Nachrichten-Konzept und wo kann ich mich einlesen?
Mir ist nichts derartiges bekannt.
Live long and pros healthy,
Martin
Hallo,
Ich meine, vor einiger Zeit (jetzt nicht mehr) wurde sogar der Ton bei Youtube ausgeschaltet (oder gar das Video gestoppt), wenn Youtube auch in einem anderen Tab gestartet wurde.
Tatsächlich??
Die Ursache kenne ich nicht, womöglich eine vorübergehende Eigenschaft des Firefox. Wer möchte schon mehrere, nicht abgestimmte Dudeleien gleichzeitig hören? Jetzt dudelts wieder fesch durcheinander.
Linuchs
normalerweise gar nicht. Dokument A gibt B einen Ausschnitt mit fest definierter Größe, in dem B sich austoben kann. Eine Interaktion zwischen beiden ist nicht vorgesehen.
Sie ist möglich, wenn Dokument A und B von demselben Server kommen (sprich: Protokoll, Hostname und Port sind identisch). Sonst nicht.
Hallo Linuchs,
das Message-Protokoll der Webworker funktioniert laut MDN auch zwischen iframe und Parent.
Ob man den Empfangshandler mit window.onmessage registriert oder doch besser mit addEventListener("message", ...), ist vermutlich Geschmackssache, aber mein Geschmack wäre: lieber allgemeingültig mit dem EventTarget-Protokoll statt mit nur einmal verwendbaren on-Attributen.
Rolf
das [Message-Protokoll der Webworker]
Verweis auf Webworker sind für die Frage IMHO nicht zielführend. Das Konzept gilt auch für "mit ohne" Webworker.
Hallo Mitleser,
wenn Du mich nur halb zitierst, kommt dabei natürlich Unsinn heraus.
"funktioniert auch zwischen iframe und parent" ist der relevante Teil des Satzes. Und der von mir verlinkte Wikiartikel beschreibt gerade postMessage und message-Event.
Vielleicht sollte ich im Wiki noch vermerken, wie man postMessage zur Interframe-Kommunikation verwendet.
Rolf
Sorry, ja. Hatte nur den Wiki-Artikel überflogen und nur „Webworker“ gesehen.
Vor gut 10 Jahren habe ich für sowas auch einige Verrenkungen betrieben. Heute:
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
Hallo Linuchs,
dieses ganze Geschwätz zwischen Parent und IFrame setzt voraus, dass die "fremde" Seite nicht wirklich fremd ist. Ein Bild von dort zu laden, dass Dir die Höhe verrät, ist ja auch eine Form der Kommunikation.
D.h. diese "fremde Seite", die deinen iframe einbettet, ist so fremd nicht. Du kannst mir dem Kollegen, der diese Seite betreut, reden. Und dieser fremde Kollege ist bereit, JavaScript einzubinden, andernfalls könnte er ja deinen iframe nicht passend resizen.
Das heißt aber auch, dass dieser Kollege bereit sein dürfte, einen Eventhandler auf "message" zu registrieren. Dorthin schickst Du aus dem iframe eine Message mit der benötigten Höhe, wenn der iframe geladen ist. Wie Du an die Höhe herankommst, ist ein anderes Problem, aber das kannst Du ja offenbar lösen, weil Du das Bild fabrizieren kannst.
Für die Kommunikation ist kaum was zu tun:
Im iframe:
let width = ...;
let height = ...;
window.parent.postMessage({ width, height }, "*");
Als Sender solltest Du keinen Origin angeben, denn Du möchtest ja nicht pro Host-Seite eine andere Version deines Kalenders haben, die sich nur im Origin unterscheidet. Du möchtest deinen Größenbedarf jedem mitteilen, der Dich einbindet.
Und beim Einbettenden:
window.addEventListener("message", function(messageEvent) {
if (messageEvent.origin == "https://remso.eu") {
let size = message.data;
kalender.style = `width: ${size.width}px; ${size.height}px;`;
}
});
Den Origin-String wirst Du anpassen müssen. Im Zweifel ausprobieren. Aber abfragen sollte man ihn, damit nicht irgendein bösartiges Script was posten kann, und damit klar ist, welches Format die gesendeten Daten haben.
Die Ablage in der size-Variablen dient vor allem dazu, dass die Zeilenbreit eim Forum nicht zu groß wird…
Und ob Du die iframe-Größe nun so oder anders setzen willst, ist ebenfalls Objekt der Spekulation 😀
Rolf
Hallo Rolf,
danke erstmal.
Mit deiner Hilfe habe ich nun das AHA-Erlebnis und ein Muster gemacht, wie sich Parent und Iframe gegenseitig Texte zusenden können. Ich schreibe den Code hier mal rein, vielleicht hilft es anderen Lesern.
let size = message.data;
let size = messageEvent.data;
iframe-Dokument "window_postmessage_iframe.php":
<body>
<br>
<p><l>an parent:</l>
<input type=text name=an_parent title=an_parent value="Text an Parent" />
<button onclick="sendeNachricht( document.getElementsByName('an_parent')[0].value )">senden</button></p>
<br>
<p><l>von parent:</l>
<input type=text name=von_parent /></p>
</body>
<script>
// ***************************
// iframe sendet an parent
// ***************************
function sendeNachricht ( text ) {
window.parent.postMessage( text );
}
// ***************************
// iframe empfängt vom parent
// ***************************/
window.addEventListener("message", function(messageEvent) {
if (messageEvent.origin == "http://osmer.de") {
document.getElementsByName("von_parent")[0].value = messageEvent.data;
}
});
</script>
parent-Dokument "window_postmessage.php":
...
<p><l>an iframe:</l>
<input type=text name=an_iframe title=an_iframe value="parent an iframe" />
<button onclick="sendeNachricht( document.getElementsByName('an_iframe')[0].value )">senden</button></p>
<br>
<p><l>von iframe:</l>
<input type=text name=von_iframe title=von_iframe value="" /></p>
...
<iframe id=postmessage_iframe src="window_postmessage_iframe.php" style="width:100%;height:10em;"></iframe>
...
<script>
// ****************************************************
// parent sendet an iframe.id = 'postmessage_iframe'
// ****************************************************
function sendeNachricht ( text ) {
console.log( 'parent sendet: ***' +text +'***' );
window.frames['postmessage_iframe'].contentWindow.postMessage( text );
}
// ***************************
// parent empfängt vom iframe
// ****************************/
window.addEventListener("message", function(messageEvent) {
if (messageEvent.origin == "http://osmer.de") {
document.getElementsByName("von_iframe")[0].value = messageEvent.data;
}
});
</script>
Gruß, Linuchs
Hallo Rolf,
Als Sender solltest Du keinen Origin angeben, denn Du möchtest ja nicht pro Host-Seite eine andere Version deines Kalenders haben, die sich nur im Origin unterscheidet.
Doch. Der Kalender kann mit unterschiedlichen Layout-Dateien (Templates) angefordert werden. Allerdings ist der Kalender ja schon ausgeliefert, wenn seine Höhe vom iframe an parent gemeldet wird.
Aber wegen der Weiterbildung:
Was bedeutet "Origin" in diesem Zusammenhang?
Gruß, Linuchs
Hallo Linuchs,
der Origin ist das Tripel aus Protokoll, Hostname und Port.
wenn https://www.example.org/letsbellow/shanty.html
deinen Kalender von https://remso.eu
einbindet, dann hat die äußere Seite den Origin https://www.example.org:443
und eingebunden wird der Origin https://remso.eu:443
(jeweils der Default-Port).
Wenn Du aus dem iframe heraus einen postMessage mit Origin "*" machst, kannst Du an jeden einbettenden schicken. Andernfalls musst Du einen konkreten Origin angeben und kannst dann nur an Einbettungen einer einzigen Domain verschicken.
Rolf
Hallo Rolf,
danke für die nachgereichte Erklärung.
Hier die Verwendung von postMessage in der Praxis: Kalender im iframe
Die Höhe des Dokuments im iframe melde ich so (html oder body ist gleichwertig):
let height = document.getElementsByTagName("html")[0].offsetHeight;
window.parent.postMessage( height, "*" );
Ich verstehe nicht, warum unterhalb des iframe-footer (© 2008-2021 ...) noch so viel height ist. Was hängt denn noch hinterm body bzw. html? Mit Element untersuchen habe ich es nicht gefunden:
Gruß, Linuchs
Hallo,
trägt jetzt leider nichts zum Thema bei, aber wie kannst du mit einer so unscharfen Textdarstellung zurechtkommen? Bei den verwaschenen Schriftkonturen würde ich Kopfschmerzen kriegen.
Live long and pros healthy,
Martin
trägt jetzt leider nichts zum Thema bei, aber wie kannst du mit einer so unscharfen Textdarstellung zurechtkommen? Bei den verwaschenen Schriftkonturen würde ich Kopfschmerzen kriegen.
Vielleicht hat er (ja, ich weiß - unwahrscheinlich) einen „4K Ultra-HD Monitor“ und der vom Forum heruntergerechnete Upload seines Screenshots bildet eine womöglich kristallklare Darstellung auf dem Schirm einfach nicht ab?
Hi there,
trägt jetzt leider nichts zum Thema bei, aber wie kannst du mit einer so unscharfen Textdarstellung zurechtkommen? Bei den verwaschenen Schriftkonturen würde ich Kopfschmerzen kriegen.
Wer sagt Dir, daß er nicht eh mit Kopfschmerzen vorm Monitor sitzt...?
Hallo klawischnigg,
bei dem CSS? Bestimmt…
SCNR
Rolf
Moin,
die Darstellung im Forum ist willkürlich verkleinert, da sind dann manche Pixel daneben.
Hier ein Original:
Dasselbe mit Gimp auf doppelte Länge vergrößert:
Hi,
die Darstellung im Forum ist willkürlich verkleinert, da sind dann manche Pixel daneben.
deswegen habe ich mir deinen Screenshot auch in Originalgröße (aus dem Forenbeitrag herausgelöst) angesehen. Dann sieht er zwar nicht mehr ganz so übel aus, aber immer noch unscharf.
Es kann aber auch sein, dass das auf dein Display abgestimmte Subpixel-Rendering bei mir einfach nicht mehr passt und daher auf meinem Monitor schlimm aussieht. Eine verkleinerte oder vergrößerte Darstellung macht es dann nicht besser.
Live long and pros healthy,
Martin
Hallo Der,
so schlimm finde ich es auch nicht. Für textlastige Bilder eigenen sich gif und png sowieso weit besser.
Gruss
Henry
Für textlastige Bilder eigenen sich gif und png sowieso weit besser.
Hatte versucht, das Bild als *.png hochzuladen. Bei "Bild auswählen" wird es verkleinert, aber komplett kontroll-angezeigt, im Beitrag aber nach etwa der halben Höhe abgeschnitten.
Ich versuche es nochmal als *.png:
Linuchs
So sieht es bei mir im FF aus, diesmal aber als *.jpg:
Hallo Linuchs,
Mit Element untersuchen habe ich es nicht gefunden:
Ich aber. Sogar im Firefox. Es ist basis.css, Zeile 320.
header::after, .main::after, footer::after {
content: "";
display: block;
clear: both;
}
Das display:block
bringt das ::after-Pseudoelement auf eine eigene Zeile und belegt die Höhe. Brauchst Du das display für irgendwas? Ich hab's weggenommen und - schwups - die Höhe hat gestimmt.
Verzichte auf Float-Layout und nimm Flexbox oder Grid um die beiden p nebeneinander zu bekommen, dann brauchst Du auch keine clear-Hacks.
Füge hinzu (bzw. integriere in die existierende footer-Regel):
footer {
display: flex;
justify-content: space-between;
}
Lösche die float-Styles für: footer p:nth-of-type(1)
, footer p:nth-of-type(2)
und nimm in Zeile 320 das footer::after
aus dem Selektor. Bzw. schmeiß die Regel ganz raus - header und .main verwenden ja keine Float.
Das ist alles. Funktioniert mit jedem relevanten Brauser (alles vor IE11 ist völlig irrelevant, IE11 ist marginal relevant). In veralteten Browsern stehen die Zeilen ggf. untereinander. So what? Die Seite funktioniert trotzdem.
Wenn Du basis.css auch für andere Seiten verwendest, dann klone die Datei, nenne den Klon basis_v2.css, ersetze alle float-Konstrukte durch Flexbox, sofern sinnvoll und möglich, und bau neue Seiten nur damit.
Rolf
Hallo Rolf,
Das display:block bringt das ::after-Pseudoelement auf eine eigene Zeile
Hatte ich im Verdacht und kontrolliert. Wenn ich im Inspektor auf ::after zeige, wird aber nur ein Strich markiert:
Linuchs
Hallo Linuchs,
das ::after-Element selbst hat auch nur eine Höhe von 0.
Aber trotzdem - ist es inline, hat der footer eine Innenhöhe von 0 (weil die gefloaten Elemente bei der Höhenberechnung nicht mitzählen).
Und ist es block, beträgt die Innenhöhe 26. Das ist die Höhe der <p> Elemente inclusive ihres Margins. Offenbar entsteht bei einem display:block im footer ein Block Formatting Context und bei display:inline nicht.
Bei genauerem Hinschauen: die folgenden Zeilen sehen harmlos aus:
<footer>
<p>© 2008-2021 Osmer Softwareentwicklung, Groß-Gerau</p>
<p><span id=info>x</span> p591b_mini | remso's gast | 0.192 sec</p>
</footer>
<script>
Aber sie sind es nicht. Zwischen </footer> und <script> ist nicht nur ein Zeilenumbruch. Vor dem <script> ist noch ein BOM  - und das ist der party pooper. Wenn ich das mit den Entwicklertools herausoperiere, verschwindet der Leerraum auch.
Offenbar wird das BOM nicht als Whitespace gerechnet, wodurch der Text eine Box erzeugt. Ich konnte das jetzt nicht mehr so richtig zu Ende probieren, weil irgendwer ein display:flex auf die Seite geschmuggelt hat 😂
Rolf
Hallo Linuchs,
weshalb Du das BOM nicht gesehen hast:
Screenshot Firefox Inspector:
Screenshot Chrome Inspector:
Firefox verrät Dir was zu flex und overflow, und Chrome verpetzt das BOM.
Rolf
Moin,
mit eurer Hilfe habe ich mich nun intensiv mit postMessage() beschäftigt.
Hatte ursprünglich angenommen, dass eine Message wie beim Rundfunk rausposaunt wird und jeder kann mithören. Wobei mir nicht ganz klar war, wer „jeder“ ist. Also auch die gleiche Webseite, die sich jemand in einer anderen Stadt anschaut? So ähnlich wie WhattsApp?
An dem Thema bin ich nämlich auch dran. Dirigent/Chorleiter kann internet-weit als Master auf die Noten seiner Slaves positionieren: Kalles Liederbuch
Inzwischen weiß ich, das kann postMessage nicht. Es kann auch nicht auf ein anderes Browser-Fenster oder einen anderen Browser-Tab zugreifen. Richtig?
postMessage ist zuständig für genau ein parent-Dukument mit (mehreren) eingebundenen iframes oder windows, die parent selbst gezeugt hat. Richtig?
postMessage kann nur an ein einziges window-object senden. Richtig?
In einer for-Schleife können nacheinander mehrere window-objects besendet werden.
iframes können keine anderen iframes besenden, sondern nur das parent-object. Das wiederum kann in einer for-Schleife alle anderen oder bestimmte andere iframes informieren. Richtig?
Hier ein Muster und die Beschreibung dazu: postmessage_parent.php
Nächstes Projekt:
Nun möchte ich gerne Liederbücher mit zahlreichen iframes so behandeln, dass <audio> Tags in allen iframes auf Pause geschaltet werden, wenn in einem beliebigen anderen iframe ein <audio> gestartet wird.
Das Konzept wäre also, dass dieses iframe the parent informiert und parent weist alle anderen iframes an, die Klappe zu halten. Richtig?
Wenn alles klar ist, wäre postmessage_parent.php vielleicht geeignet fürs selfHTML Wiki?
Gruß, Linuchs
Hallo Linuchs,
postMessage ist Kommunikation innerhalb eines einzelnen Browsers auf einer einzelnen Maschine.
Entfernen wir mal einen der Lüge für Kinder Schleier über der Sache.
postMessage kann man anwenden auf: Window, Worker und MessagePort Objekte. Was ist ein MessagePort? Wenn Du ein Fenster oder einen iframe erzeugst, wird automatisch ein MessageChannel von Dir zum neuen Fenster/iframe erzeugt. Dieser Channel besteht aus zwei gekoppelten Ports. Was Du in den einen Port hineinpostest, kommt am anderen als message-Event hinaus.
window.postMessage und worker.postMessage verwenden die Ports des Default-MessageChannel. Du kannst mit new MessageChannel aber auch einen eigenen erzeugen und einen der beiden Ports mit dem transfer-Parameter von postMessage an ein anderes Window oder einen Worker übertragen.
Das ist von Vorteil, wenn man diese Channels intensiv nutzt. Ohne private Channels müsste jegliche Kommunikation über den Default-Channel laufen und vom message-Eventhandler verteilt werden. Bei einer Mashup-Seite, die aus vielen Komponenten besteht, ist das sehr komplex. Wenn die Komponente A im Window 1 nur mit der Komponente B im Window 2 reden soll, dann ist es praktischer, wenn die beiden einen privaten Message-Channel bekommen. Auf Window-Ebene brauchst Du dann nur noch einen Mechanismus zum Anfordern und Verteilen von privaten Channels.
postMessage kann nur an ein einziges window-object senden. Richtig?
Grundsätzlich richtig. Genauer formuliert: in einen einzigen MessageChannel hineinpusten - äh - posten. Und dessen anderer Port kann nur einem einzigen globalen Kontext (=Window oder Worker) gehören.
In einer for-Schleife können nacheinander mehrere window-objects besendet werden.
Logisch
iframes können keine anderen iframes besenden, sondern nur das parent-object.
Das hast Du richtig gemeint, aber es ist unvollständig und ich möchte es genauer formulieren. Wenn man den Default-MessageChannel verwendet, kann man ohne weiteres nur an sein direktes Elternelement oder an die direkten Kindelemente senden.
Bei gleichem Origin kann ein iframe auf sein parent-Window zugreifen, und ich würde annehmen (man muss es ausprobieren), dass er dann auch an die frames
Eigenschaft herankommt und an andere Child-Windows etwas posten kann.
Bei verschiedenem Origin geht das nicht, aber - auch das ist unprobiert - das Haupt-Window kann natürlich mithelfen und über postMessage eine Funktion bereitstellen, die einen MessageChannel erzeugt, davon einen Port an den Besteller zurückschickt und den anderen Port an einen anderen iframe. Daraufhin hätten die beiden iframes einen MessageChannel, über den sie sich direkt unterhalten können.
Das Konzept wäre also, dass dieses iframe the parent informiert und parent weist alle anderen iframes an, die Klappe zu halten. Richtig?
Sehe ich auch so. Wenn Du einen parent hast, der einen Chor aus iframes orchestriert, dann ist es besser, wenn das Parent das Routing übernimmt und nicht ein iframe wissen muss, wer da alles auf der Seite mitschwimmt.
Das im Wiki mit Beispiel einzubauen könnte schwierig werden. Zumindest dürfte sich das schlecht Frickln lassen...
Rolf
Hallo Linuchs,
da war noch was.
Inzwischen weiß ich, das kann postMessage nicht. Es kann auch nicht auf ein anderes Browser-Fenster oder einen anderen Browser-Tab zugreifen. Richtig?
Nicht richtig. Du kannst über window.open oder durch Klick auf einen Link mit target-Attribut ein Popup-Fenster oder ein neues Tab erzeugen. Dieses neue Tab findet unter window.opener eine Referenz auf das Window-Objekt, von dem es erstellt wurde, und kann sich mittels postMessage dort melden.
Andersrum bekommt der Aufrufer von window.open auch eine Referenz auf das neu erstellte Window-Objekt und kann seinerseits auch dorthin posten. Der erste Post sollte aber vom neuen Window/Tab kommen, damit sichergestellt ist, dass dort das DOM geladen ist und die Scripte aktiv sind.
Achso. Ich hatte auch schon einen Spielplatz gebaut. Ist nur nicht so schick wie deiner (Send to worker ist noch todo, und channel to popup fehlt noch).
Rolf