Aufrufen von JavaScript-Code in einem Iframe von der übergeordneten Seite

615

Grundsätzlich habe ich eine iframein eine Seite eingebettete und iframeeinige JavaScript- Routinen, die ich von der übergeordneten Seite aus aufrufen muss.

Jetzt ist das Gegenteil ganz einfach, da Sie nur anrufen müssen parent.functionName(), aber leider brauche ich genau das Gegenteil davon.

Bitte beachten Sie, dass mein Problem nicht darin besteht, die Quell- URL von zu ändern iframe, sondern eine in der iframe.

Tamas Czinege
quelle
42
Ihr Hinweis zu parent.fuctioName () hat mein Problem beim Aufrufen einer Funktion im übergeordneten HTML-Code des Iframes gelöst. Danke!
CyberFonic
1
Beachten Sie beim Debuggen, dass Sie beispielsweise window.location.href oder parent.location.href verwenden können, um die URL des Iframes anzuzeigen, wenn Sie überprüfen möchten, ob Sie einen Verweis auf den gesuchten Iframe haben.
AaronLS
Siehe @le dorfiers Antwort ... hat perfekt für mich funktioniert.
ErickBest

Antworten:

542

Angenommen, die ID Ihres iFrames lautet "targetFrame" und die Funktion, die Sie aufrufen möchten, lautet targetFunction():

document.getElementById('targetFrame').contentWindow.targetFunction();

Sie können auf den Frame auch mit window.framesanstelle von zugreifen document.getElementById.

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 
Joel Anair
quelle
3
funktioniert in FF 7, Chrome 12, IE 8/9 und Safari (nicht sicher über diese Version)
Darcy
3
Diese Lösung funktioniert nicht für mein Gadget. Hier ist mein Code. document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"Remote_iframe_0 wird programmatisch von einem Apache-Shindig-Server erstellt, window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"funktioniert aber
J Bourne
3
@Dirty Henry was meinst du mit "einer jQuery-Funktion"? jQuery ist eine JavaScript-Bibliothek.
Joel Anair
8
@JellicleCat Das liegt daran, dass die übergeordnete Seite und der Iframe unterschiedliche Hosts haben, was es zu einem originensübergreifenden Frame macht, wie die Nachricht sagt. Solange das Protokoll, die Domäne und der Port der übergeordneten Seite und des Iframes übereinstimmen, funktioniert alles einwandfrei.
Mark Amery
1
Ich habe ein Beispiel zur Verwendung der erwähnten window.frames-Methode hinzugefügt.
Elijah Lynn
145

Hier sind einige Macken zu beachten.

  1. HTMLIFrameElement.contentWindowist wahrscheinlich der einfachere Weg, aber es ist keine Standard-Eigenschaft und einige Browser unterstützen sie nicht, meistens ältere. Dies liegt daran, dass der DOM Level 1 HTML-Standard nichts zu dem windowObjekt zu sagen hat .

  2. Sie können auch versuchen HTMLIFrameElement.contentDocument.defaultView, was einige ältere Browser zulassen, der IE jedoch nicht. Trotzdem sagt der Standard nicht ausdrücklich, dass Sie das windowObjekt aus dem gleichen Grund wie (1) zurückerhalten, aber Sie können hier ein paar zusätzliche Browserversionen abrufen, wenn Sie möchten.

  3. window.frames['name']Das Zurückgeben des Fensters ist die älteste und damit zuverlässigste Schnittstelle. Sie müssen dann jedoch ein name="..."Attribut verwenden, um einen Frame nach Namen zu erhalten, der leicht hässlich / veraltet / vorübergehend ist. ( id="..."wäre besser, aber IE mag das nicht.)

  4. window.frames[number]ist auch sehr zuverlässig, aber den richtigen Index zu kennen, ist der Trick. Sie können damit z. Wenn Sie wissen, dass Sie nur den einen Iframe auf der Seite haben.

  5. Es ist durchaus möglich, dass der untergeordnete Iframe noch nicht geladen wurde oder dass etwas anderes schief gelaufen ist, um den Zugriff unzugänglich zu machen. Möglicherweise fällt es Ihnen leichter, den Kommunikationsfluss umzukehren: Lassen Sie den untergeordneten Iframe sein window.parentSkript benachrichtigen, wenn es vollständig geladen wurde und zurückgerufen werden kann. Durch Übergeben eines eigenen Objekts (z. B. einer Rückruffunktion) an das übergeordnete Skript kann dieses übergeordnete Objekt direkt mit dem Skript im Iframe kommunizieren, ohne sich Gedanken darüber machen zu müssen, mit welchem ​​HTMLIFrameElement es verknüpft ist.

Bobince
quelle
13
Tolles Zeug! Besonders Ihr Vorschlag 5. hat sich als äußerst wertvoll erwiesen. Es löste viele Kopfschmerzen, als ich versuchte, über das übergeordnete Element in Chrome auf iFrame-Funktionen zuzugreifen. Durch das Übergeben des Funktionsobjekts aus dem iFrame wurde es in Chrome, FF und sogar im IE von 9 bis 7 wie ein Zauber ausgeführt und es war sehr einfach zu überprüfen, ob die Funktion bereits geladen wurde. Vielen Dank!
Jpsy
Nummer 3 funktioniert, aber besser, wenn Sie es so machen, wie @le dorfier es in seinen Kommentar einfügt. Beachten Sie das methode()Teil.
ErickBest
Bitte beachten Sie auch, dass es aufgrund von Sicherheitsbeschränkungen in modernen Browsern (getestet FF29 und Chrome34) sehr wichtig ist, wo Sie Ihren Funktionsaufruf platzieren. Das Aufrufen der Funktion über ein Skript-Tag oder $ (document) .ready () funktioniert nicht. Platzieren Sie den Anruf stattdessen im Onload-Tag des Körpers oder in einem Anker <a href="javascript:doit();">, wie an anderer Stelle vorgeschlagen (siehe stackoverflow.com/questions/921387/… ). Das Übergeben des Funktionsaufrufs an ein Skript als Rückruf funktioniert normalerweise ebenfalls.
Titusn
@chovy: Nein, siehe die Klarstellung in Viveks Antwort dazu.
Bobince
66

Das Aufrufen einer übergeordneten JS-Funktion von iframeist möglich, jedoch nur, wenn sowohl die übergeordnete JS-Funktion als auch die in die geladene Seite iframeaus derselben Domäne stammen, z. B. abc.com, und beide dasselbe Protokoll verwenden, dh beide entweder aktiviert sind http://oder https://.

Der Anruf schlägt in den folgenden Fällen fehl:

  1. Die übergeordnete Seite und die Iframe-Seite stammen aus verschiedenen Domänen.
  2. Sie verwenden unterschiedliche Protokolle, eines befindet sich unter http: // und das andere unter https: //.

Eine Umgehung dieser Einschränkung wäre äußerst unsicher.

Stellen Sie sich zum Beispiel vor, ich hätte die Domain superwinningcontest.com registriert und Links zu E-Mails von Personen gesendet. Wenn sie die Hauptseite geladen haben, konnte ich ein paar iframeSekunden dort verstecken und ihren Facebook-Feed lesen, die letzten Amazon- oder PayPal-Transaktionen überprüfen oder - wenn sie einen Dienst nutzten, der keine ausreichende Sicherheit implementierte - Geld von ihnen überweisen Konten. Aus diesem Grund ist JavaScript auf dieselbe Domäne und dasselbe Protokoll beschränkt.

Vivek Singh CHAUHAN
quelle
6
Die Problemumgehung besteht darin, die schöne und gefährliche en.wikipedia.org/wiki/Cross-document_messaging
Laramie
Weiß jemand, ob eine andere Subdomain dazu führt, dass dies fehlschlägt? Ich habe Schwierigkeiten beim Debuggen eines Problems, von dem ich glaube, dass es damit zusammenhängt - der Iframe ruft eine übergeordnete Fensterfunktion auf, aber der Aufruf findet nicht statt.
Jake
1
Beachten Sie, dass dies auch für verschiedene Portnummern fehlschlagen würde. dh abc.com:80! = abc.com:8080
prasanthv
31

Machen Sie im IFRAME Ihre Funktion für das Fensterobjekt öffentlich:

window.myFunction = function(args) {
   doStuff();
}

Verwenden Sie für den Zugriff von der übergeordneten Seite Folgendes:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);
Tomalak
quelle
Gute Antwort. Ich bin mir der Konvention hier nicht sicher. Ich habe eine Folgefrage. Wenn ich eine neue Frage stellen sollte, werde ich. Ich habe ein Problem. Der Iframe meiner Seite wird dynamisch gefüllt. Es enthält eine Schaltfläche, deren onclick()Ereignis einfach aufruft window.print(). Dies druckt den Inhalt des iframeperfekt. Wenn jedoch die öffentliche Funktion der Iframe-Seite aufgerufen wird window.print()und die übergeordnete Seite die öffentliche Funktion aufruft, wird der Inhalt der übergeordneten Seite gedruckt. Wie soll ich das codieren, damit sich der Aufruf von window.print()in der öffentlichen Funktion wie im onclickEreignis verhält ?
Karl
1
@ Karl Du willst nicht anrufen onclick. Sie möchten anrufen iframe.contentWindow.print().
Tomalak
Leider geht das nicht. Vielleicht sollte ich eine neue Frage stellen und meinen Code dokumentieren? In beiden Fällen wird auch der Inhalt der übergeordneten Seite gedruckt. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent () `(nicht der Oncick-Ereignishandler), der aufruft, window.print()wurde folgendermaßen aufgerufen:document.getElementById('myFrame').contentWindow.printContent();
Karl
1
@ Karl - Ja, dann ist es am besten, eine neue Frage zu beginnen. Es sei denn, das übergeordnete Fenster befindet sich in einer anderen Domäne als der Iframe. Dann wird nichts, was Sie versuchen, jemals funktionieren .
Tomalak
Was ich hier gemeldet habe, scheint ein IE-Problem zu sein (Testen in IE8). Kein Problem in der aktuellen Version von Chrome. Habe aber noch keine anderen Browser getestet. Für Interessierte siehe diese jsFiddle (ich denke, dies ist ein gutes Beispiel für das dynamische Laden von iframes und das Aufrufen einer öffentlichen Funktion vom Elternteil.)
Karl
20

Wenn sich das Ziel des iFrames und das enthaltende Dokument in einer anderen Domäne befinden, funktionieren die zuvor veröffentlichten Methoden möglicherweise nicht, es gibt jedoch eine Lösung:

Wenn beispielsweise Dokument A ein iframe-Element enthält, das Dokument B enthält, und das Skript in Dokument A postMessage () für das Window-Objekt von Dokument B aufruft, wird ein Nachrichtenereignis für dieses Objekt ausgelöst, das als aus dem Fenster von stammend markiert ist Dokument A. Das Skript in Dokument A könnte folgendermaßen aussehen:

var o = document.getElementsByTagName('iframe')[0];
o.contentWindow.postMessage('Hello world', 'http://b.example.org/');

Um einen Ereignishandler für eingehende Ereignisse zu registrieren, verwendet das Skript addEventListener () (oder ähnliche Mechanismen). Das Skript in Dokument B könnte beispielsweise folgendermaßen aussehen:

window.addEventListener('message', receiver, false);
function receiver(e) {
  if (e.origin == 'http://example.com') {
    if (e.data == 'Hello world') {
      e.source.postMessage('Hello', e.origin);
    } else {
      alert(e.data);
    }
  }
}

Dieses Skript überprüft zunächst, ob die Domäne die erwartete Domäne ist, und überprüft dann die Nachricht, die entweder dem Benutzer angezeigt wird, oder sendet daraufhin eine Nachricht an das Dokument zurück, das die Nachricht zuerst gesendet hat.

über http://dev.w3.org/html5/postmsg/#web-messaging

19greg96
quelle
1
easyXDM bietet eine Abstraktion über PostMessage für ältere Browser (einschließlich eines Flash-Fallbacks (LocalConnection) für hartnäckige Dinosaurier).
JonnyReeves
1
das ist großartig ... nachdem ich die obigen Antworten ausprobiert habe, habe ich endlich deine Antwort bekommen, danke!
vkGunasekaran
9

Nur zur Veranschaulichung, ich bin heute auf dasselbe Problem gestoßen, aber dieses Mal wurde die Seite in ein Objekt eingebettet, nicht in einen Iframe (da es sich um ein XHTML 1.1-Dokument handelte). So funktioniert es mit Objekten:

document
  .getElementById('targetFrame')
  .contentDocument
  .defaultView
  .targetFunction();

(Entschuldigung für die hässlichen Zeilenumbrüche, passte nicht in eine einzelne Zeile)

Tamas Czinege
quelle
8

Der IFRAME sollte sich in der frames[]Sammlung befinden. Verwenden Sie so etwas wie

frames['iframeid'].method();
dkretz
quelle
Diese Antwort könnte sicherlich einige weitere Informationen gebrauchen, da es definitiv einige andere Überlegungen gibt. Zum einen gehe ich davon aus, dass der Entwickler methodeine Eigenschaft des iframe- windowObjekts erstellen muss .
Matty
8

Quirksmode hatte einen Beitrag dazu .

Da die Seite jetzt kaputt ist und nur über archive.org zugänglich ist, habe ich sie hier reproduziert:

IFrames

Auf dieser Seite gebe ich einen kurzen Überblick über den Zugriff auf Iframes von der Seite, auf der sie sich befinden. Es überrascht nicht, dass es einige Überlegungen zum Browser gibt.

Ein Iframe ist ein Inline-Frame, ein Frame, der zwar eine vollständig separate Seite mit einer eigenen URL enthält, jedoch in einer anderen HTML-Seite platziert ist. Dies gibt sehr schöne Möglichkeiten im Webdesign. Das Problem besteht darin, auf den Iframe zuzugreifen, um beispielsweise eine neue Seite darin zu laden. Diese Seite erklärt, wie es geht.

Rahmen oder Objekt?

Die grundlegende Frage ist, ob der Iframe als Frame oder als Objekt gesehen wird.

  • Wie in der Einführung zu Frames erläutert , erstellt der Browser bei Verwendung von Frames eine Frame-Hierarchie für Sie (top.frames[1].frames[2] und dergleichen). Passt der Iframe in diese Frame-Hierarchie?
  • Oder sieht der Browser einen Iframe nur als ein anderes Objekt, ein Objekt, das zufällig eine src-Eigenschaft hat? In diesem Fall müssen wir einen Standard- DOM-Aufruf verwenden ( document.getElementById('theiframe'))um darauf zuzugreifen. Im Allgemeinen erlauben Browser beide Ansichten für "echte" (fest codierte) Iframes, aber generierte Iframes können nicht als Frames aufgerufen werden.

NAME-Attribut

Die wichtigste Regel ist, jedem Iframe, den Sie erstellen, ein nameAttribut zu geben, auch wenn Sie auch ein verwenden id.

<iframe src="iframe_page1.html"
    id="testiframe"
    name="testiframe"></iframe>

Die meisten Browser benötigen das nameAttribut, um den Iframe in die Frame-Hierarchie aufzunehmen. Einige Browser (insbesondere Mozilla) benötigen den id, um den Iframe als Objekt zugänglich zu machen. Indem Sie dem Iframe beide Attribute zuweisen, bleiben Ihre Optionen offen. Ist nameaber weitaus wichtiger alsid .

Zugriff

Entweder greifen Sie als Objekt auf den Iframe zu und ändern ihn, srcoder Sie greifen als Frame auf den Iframe zu und ändern ihn location.href.

document.getElementById ('iframe_id'). src = 'newpage.html'; frame ['iframe_name']. location.href = 'newpage.html'; Die Rahmensyntax ist etwas vorzuziehen, da Opera 6 sie unterstützt, nicht jedoch die Objektsyntax.

Zugriff auf den Iframe

Für eine vollständige browserübergreifende Erfahrung sollten Sie dem iframe einen Namen geben und den verwenden

frames['testiframe'].location.href

Syntax. Soweit ich weiß, funktioniert das immer.

Zugriff auf das Dokument

Der Zugriff auf das Dokument im Iframe ist recht einfach, sofern Sie das nameAttribut verwenden. Führen Sie die folgenden Schritte aus, um die Anzahl der Links im Dokument im Iframe zu zählen frames['testiframe'].document.links.length.

Iframes generiert

Wenn Sie einen Iframe über das W3C-DOM generieren, wird der Iframe nicht sofort in das eingegebenframes Array , und die frames['testiframe'].location.hrefSyntax funktioniert nicht sofort. Der Browser benötigt einige Zeit, bis der Iframe im Array angezeigt wird. Während dieser Zeit kann möglicherweise kein Skript ausgeführt werden.

Das document.getElementById('testiframe').src Syntax funktioniert unter allen Umständen einwandfrei.

Das targetAttribut eines Links funktioniert auch nicht mit generierten Iframes, außer in Opera, obwohl ich meinem generierten Iframe sowohl ein nameals auch ein gegeben habeid .

Der Mangel an target Unterstützung bedeutet, dass Sie JavaScript verwenden müssen, um den Inhalt eines generierten Iframes zu ändern. Da Sie jedoch ohnehin JavaScript benötigen, um ihn zu generieren, sehe ich dies nicht als großes Problem an.

Textgröße in Iframes

Ein merkwürdiger Explorer 6-Fehler:

Wenn Sie die Textgröße über das Menü Ansicht ändern, werden die Textgrößen in Iframes korrekt geändert. Dieser Browser ändert jedoch nicht die Zeilenumbrüche im Originaltext, sodass ein Teil des Textes möglicherweise unsichtbar wird oder Zeilenumbrüche auftreten können, während die Zeile noch ein anderes Wort enthalten kann.

activout.se
quelle
5
Dieser Link scheint unterbrochen zu sein.
Zaphod
1
Und um die Sache noch schlimmer zu machen, kann ich die verwandte Seite nirgends im Quirksmode finden.
Stricjux
7

Ich habe eine ziemlich elegante Lösung gefunden.

Wie Sie sagten, ist es ziemlich einfach, Code auszuführen, der sich im übergeordneten Dokument befindet. Und das ist die Basis meines Codes. Machen Sie genau das Gegenteil.

Wenn mein Iframe geladen wird, rufe ich eine Funktion auf, die sich im übergeordneten Dokument befindet, und übergebe als Argument einen Verweis auf eine lokale Funktion, die sich im Iframe-Dokument befindet. Das übergeordnete Dokument hat jetzt über diese Referenz direkten Zugriff auf die Funktion des Iframes.

Beispiel:

Auf dem Elternteil:

function tunnel(fn) {
    fn();
}

Auf dem Iframe:

var myFunction = function() {
    alert("This work!");
}

parent.tunnel(myFunction);

Wenn der Iframe geladen wird, ruft er parent.tunnel (YourFunctionReference) auf, wodurch die im Parameter empfangene Funktion ausgeführt wird.

So einfach, ohne sich mit allen Nicht-Standard-Methoden der verschiedenen Browser auseinandersetzen zu müssen.

Julien L.
quelle
Hallo, können Sie dies bestätigen? "So einfach, ohne sich mit allen nicht standardmäßigen Methoden der verschiedenen Browser befassen zu müssen."
Milche Patern
1
Ich benutze diese Methode bei verschiedenen Projekten, scheint sehr gut zu funktionieren. Ich habe nicht alle im Umlauf befindlichen Browser persönlich getestet, aber ich habe auch keinen Fehlerbericht darüber erhalten.
Julien L
Funktioniert wie ein Zauber, IE 11, Chrome ~ 50.
Augustas
1
Vermutlich ist das nicht domänenübergreifend.
Tushar Shukla
@TusharShukla Leider nein, ist es nicht.
Jeff-h
6

Einige dieser Antworten behandeln das CORS-Problem nicht oder machen nicht deutlich, wo Sie die Codefragmente platzieren, um die Kommunikation zu ermöglichen.

Hier ist ein konkretes Beispiel. Angenommen, ich möchte auf der übergeordneten Seite auf eine Schaltfläche klicken und diese im Iframe ausführen lassen. Hier ist, wie ich es machen würde.

parent_frame.html

<button id='parent_page_button' onclick='call_button_inside_frame()'></button>

function call_button_inside_frame() {
   document.getElementById('my_iframe').contentWindow.postMessage('foo','*');
}

iframe_page.html

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
    {
      if(event) {
        click_button_inside_frame();
    }
}

function click_button_inside_frame() {
   document.getElementById('frame_button').click();
}

Um in die andere Richtung zu gehen (klicken Sie auf die Schaltfläche innerhalb des Iframes, um die Methode außerhalb des Iframes aufzurufen), wechseln Sie einfach, wo sich das Code-Snippet befindet, und ändern Sie dies:

document.getElementById('my_iframe').contentWindow.postMessage('foo','*');

dazu:

window.parent.postMessage('foo','*')
Kybernetisch
quelle
4

Fortsetzung der Antwort von JoelAnair :

Verwenden Sie für mehr Robustheit Folgendes:

var el = document.getElementById('targetFrame');

if(el.contentWindow)
{
   el.contentWindow.targetFunction();
}
else if(el.contentDocument)
{
   el.contentDocument.targetFunction();
}

Arbeitete wie Charme :)

Nitin Bansal
quelle
3

Nach der Antwort von Nitin Bansal

und für noch mehr Robustheit:

function getIframeWindow(iframe_object) {
  var doc;

  if (iframe_object.contentWindow) {
    return iframe_object.contentWindow;
  }

  if (iframe_object.window) {
    return iframe_object.window;
  } 

  if (!doc && iframe_object.contentDocument) {
    doc = iframe_object.contentDocument;
  } 

  if (!doc && iframe_object.document) {
    doc = iframe_object.document;
  }

  if (doc && doc.defaultView) {
   return doc.defaultView;
  }

  if (doc && doc.parentWindow) {
    return doc.parentWindow;
  }

  return undefined;
}

und

...
var el = document.getElementById('targetFrame');

var frame_win = getIframeWindow(el);

if (frame_win) {
  frame_win.targetFunction();
  ...
}
...
Dominique Fortin
quelle
2
       $("#myframe").load(function() {
            alert("loaded");
        });
GeverGever
quelle
2

Gleiche Dinge, aber ein bisschen einfacher wird es sein, wie man eine übergeordnete Seite von einer Seite innerhalb des Iframes aktualisiert . Rufen Sie einfach die Funktion der übergeordneten Seite auf, um die Javascript-Funktion aufzurufen und die Seite neu zu laden:

window.location.reload();

Oder tun Sie dies direkt von der Seite in iframe:

window.parent.location.reload();

Beides funktioniert.

Sangam
quelle
1

Versuchen Sie es einfach parent.myfunction()

Saeid Zebardast
quelle
@Saeid Das OP möchte eine Funktion in einem Iframe aus dem übergeordneten Fenster aufrufen, nicht umgekehrt.
Jean-François Beauchamp
1

Wenn Sie die JavaScript-Funktion auf dem übergeordneten Element über den Iframe aufrufen möchten, der von einer anderen Funktion (z. B. Shadowbox oder Lightbox) generiert wurde.

Sie sollten versuchen, das windowObjekt zu verwenden und die übergeordnete Funktion aufzurufen:

window.parent.targetFunction();
Santhanakumar
quelle
Ich glaube, das OP versucht, in die andere Richtung zu gehen
CommandZ
-3

Verwenden Sie Folgendes, um die Funktion eines Frames auf der übergeordneten Seite aufzurufen

parent.document.getElementById('frameid').contentWindow.somefunction()
Sandig
quelle