Invocación de código JavaScript en un iframe desde la página principal

615

Básicamente, tengo un iframeincrustado en una página y iframetiene algunas rutinas de JavaScript que necesito invocar desde la página principal.

Ahora lo contrario es bastante simple, ya que solo necesita llamar parent.functionName(), pero desafortunadamente, necesito exactamente lo contrario.

Tenga en cuenta que mi problema no es cambiar la URL de origen de la iframe, sino invocar una función definida en iframe.

Tamas Czinege
fuente
42
Su nota sobre parent.fuctioName () resolvió mi problema de llamar a una función en el html padre del iframe. Gracias!
CyberFonic
1
Tenga en cuenta que al depurar puede hacer cosas como window.location.href o parent.location.href para ver la url del iframe, si desea verificar que tiene una referencia al iframe que está buscando.
AaronLS
Ver la respuesta de @le dorfier ... funcionó perfectamente para mí.
ErickBest

Respuestas:

542

Suponga que la identificación de su iFrame es "targetFrame" y la función que desea llamar es targetFunction():

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

También puede acceder al marco usando en window.frameslugar de document.getElementById.

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 
Joel Anair
fuente
3
funciona en FF 7, Chrome 12, IE 8/9 y Safari (no estoy seguro de esta versión)
Darcy
3
Esta solución no funciona para mi dispositivo, aquí está mi código document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"remote_iframe_0 es creado programáticamente por un servidor apache shindig pero window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"funciona
J Bourne
3
@Dirty Henry, ¿qué quieres decir con "una función jQuery"? jQuery es una biblioteca de JavaScript.
Joel Anair 01 de
8
@JellicleCat Eso se debe a que la página principal y el iframe tienen hosts diferentes, lo que lo convierte en un marco de origen cruzado, como lo indica el mensaje. Mientras el protocolo, el dominio y el puerto de la página principal y el iframe coincidan, todo funcionará bien.
Mark Amery
1
Agregué un ejemplo sobre cómo usar el método window.frames mencionado.
Elijah Lynn
145

Hay algunas peculiaridades a tener en cuenta aquí.

  1. HTMLIFrameElement.contentWindowes probablemente la forma más fácil, pero no es una propiedad estándar y algunos navegadores no la admiten, en su mayoría los más antiguos. Esto se debe a que el estándar HTML DOM Nivel 1 no tiene nada que decir sobre el windowobjeto.

  2. También puede probar HTMLIFrameElement.contentDocument.defaultView, lo que permite un par de navegadores más antiguos, pero IE no. Aun así, el estándar no dice explícitamente que recuperas el windowobjeto, por la misma razón que (1), pero puedes elegir algunas versiones adicionales del navegador aquí si te importa.

  3. window.frames['name']devolver la ventana es la interfaz más antigua y, por lo tanto, la más confiable. Pero luego debe usar un name="..."atributo para poder obtener un marco por nombre, que es ligeramente feo / obsoleto / transicional. ( id="..."sería mejor, pero a IE no le gusta eso)

  4. window.frames[number]También es muy confiable, pero conocer el índice correcto es el truco. Puede salirse con la suya, por ejemplo. si sabe que solo tiene un iframe en la página.

  5. Es completamente posible que el iframe secundario no se haya cargado todavía, o que algo más salió mal para hacerlo inaccesible. Es posible que le resulte más fácil revertir el flujo de comunicaciones: es decir, haga que el iframe secundario notifique su window.parentsecuencia de comandos cuando haya terminado de cargarse y esté listo para ser devuelto. Al pasar uno de sus propios objetos (por ejemplo, una función de devolución de llamada) al script principal, ese padre puede comunicarse directamente con el script en el iframe sin tener que preocuparse de con qué HTMLIFrameElement está asociado.

bobince
fuente
13
¡Buena cosa! Especialmente su sugerencia 5. ha demostrado ser extremadamente valiosa. Me resolvió muchos dolores de cabeza al intentar acceder a las funciones de iFrame desde el padre en Chrome. Pasar el objeto de función desde el iFrame lo hizo funcionar como un encanto en Chrome, FF e incluso IE de 9 a 7 y también hizo que fuera muy fácil verificar si la función ya se había cargado. ¡Gracias!
Jpsy
El número 3 funciona, pero mejor si lo haces como @le dorfier ponlo en su comentario. Note la methode()parte.
ErickBest
Tenga en cuenta también que debido a las restricciones de seguridad en los navegadores modernos (probado FF29 y Chrome34), importa mucho dónde realiza su llamada de función. Simplemente llamar a la función desde una etiqueta de script o $ (document) .ready () no funcionará. En su lugar, coloque la llamada dentro de la etiqueta de carga del cuerpo, o en un ancla <a href="javascript:doit();"> hágalo </a> como se sugiere en otro lugar (consulte stackoverflow.com/questions/921387/… ). Pasar la llamada de función a un script como devolución de llamada también suele funcionar.
titusn
@chovy: No, vea la aclaración en la respuesta de Vivek sobre esto.
bobince
66

Es posible llamar a una función JS principal desde iframe, pero solo cuando el principal y la página cargada en el iframemismo son del mismo dominio, es decir, abc.com, y ambos utilizan el mismo protocolo, es decir, ambos están en http://o https://.

La llamada fallará en los casos mencionados a continuación:

  1. La página principal y la página de iframe son de dominio diferente.
  2. Están utilizando diferentes protocolos, uno está en http: // y otro está en https: //.

Cualquier solución a esta restricción sería extremadamente insegura.

Por ejemplo, imagina que registré el dominio superwinningcontest.com y envié enlaces a los correos electrónicos de las personas. Cuando cargaron la página principal, pude esconder algunos iframecorreos electrónicos allí y leer su feed de Facebook, verificar las transacciones recientes de Amazon o PayPal o, si usaban un servicio que no implementaba suficiente seguridad, transferir dinero de su cuenta. cuentas Es por eso que JavaScript está limitado al mismo dominio y al mismo protocolo.

Vivek Singh CHAUHAN
fuente
66
La solución consiste en utilizar el hermoso y peligroso en.wikipedia.org/wiki/Cross-document_messaging
Laramie
¿Alguien sabe si un subdominio diferente hará que esto falle? Tengo dificultades para depurar un problema que creo está relacionado con esto: el iframe está llamando a una función de ventana principal, pero la llamada no está ocurriendo.
Jake
1
Tenga en cuenta que también fallaría para diferentes números de puerto. es decir, abc.com:80! = abc.com:8080
prasanthv
31

En el IFRAME, haga que su función sea pública para el objeto de ventana:

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

Para acceder desde la página principal, use esto:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);
Tomalak
fuente
Gran respuesta. No estoy seguro de la convención aquí. Tengo una pregunta de seguimiento. Si debo comenzar una nueva pregunta, lo haré. Tengo un problema El iframe de mi página se llena dinámicamente. Contiene un botón cuyo onclick()evento simplemente llama window.print(). Esto imprime el contenido de la iframeperfección. Pero cuando la función pública de la página del iframe llama window.print()y si la página primaria llama a la función pública, se imprime el contenido de la página principal. ¿Cómo debo codificar esto para que la llamada a window.print()la función pública se comporte como lo hace en el onclickevento?
Karl
1
@Karl No quieres llamar onclick. Al que desea llamar iframe.contentWindow.print().
Tomalak
Lamentablemente, eso no funciona. ¿Quizás debería comenzar una nueva pregunta y documentar mi código? De cualquier manera, el contenido de la página principal también se imprime. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent () `(no el controlador de eventos oncick) que llama window.print()se llamó de esta manera:document.getElementById('myFrame').contentWindow.printContent();
Karl
1
@Karl - Sí, entonces es mejor comenzar una nueva pregunta. A menos que la ventana principal esté en un dominio diferente que el iframe. Entonces, nada de lo que intentes funcionará .
Tomalak
Lo que informé aquí parece ser un problema de IE (prueba en IE8). No hay problema en la versión actual de Chrome. Sin embargo, aún no he probado otros navegadores. Para aquellos interesados, vea este jsFiddle (creo que este es un buen ejemplo de carga dinámica de iframe y de llamar a una función pública desde el padre).
Karl
20

Si el objetivo del iFrame y el documento que lo contiene están en un dominio diferente, los métodos publicados anteriormente podrían no funcionar, pero hay una solución:

Por ejemplo, si el documento A contiene un elemento iframe que contiene el documento B, y el script en el documento A llama a postMessage () en el objeto Window del documento B, entonces se disparará un evento de mensaje en ese objeto, marcado como originario de la ventana de documento A. El script en el documento A podría verse así:

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

Para registrar un controlador de eventos para eventos entrantes, el script usaría addEventListener () (o mecanismos similares). Por ejemplo, el script en el documento B podría verse así:

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);
    }
  }
}

Este script primero comprueba que el dominio es el dominio esperado, y luego mira el mensaje, que se muestra al usuario o responde enviando un mensaje al documento que envió el mensaje en primer lugar.

a través de http://dev.w3.org/html5/postmsg/#web-messaging

19greg96
fuente
1
easyXDM proporciona una abstracción sobre PostMessage para navegadores más antiguos (incluido un respaldo Flash (LocalConnection) para los tercos dinosaurios).
JonnyReeves
1
eso es genial ... después de probar las respuestas anteriores finalmente obtuve tu respuesta, ¡gracias!
vkGunasekaran
9

Solo para el registro, me encontré con el mismo problema hoy, pero esta vez la página estaba incrustada en un objeto, no en un iframe (ya que era un documento XHTML 1.1). Así es como funciona con objetos:

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

(perdón por los saltos de línea feos, no cabía en una sola línea)

Tamas Czinege
fuente
8

El IFRAME debe estar en la frames[]colección. Usa algo como

frames['iframeid'].method();
dkretz
fuente
Esta respuesta ciertamente podría usar más información, ya que definitivamente hay algunas otras consideraciones. Por un lado, supongo que el desarrollador necesita hacer methoduna propiedad del windowobjeto del iframe .
Matty
8

Quirksmode tenía una publicación sobre esto .

Como la página ahora está rota y solo se puede acceder a través de archive.org, la reproduje aquí:

IFrames

En esta página, proporciono una breve descripción general del acceso a los iframes desde la página en la que se encuentran. No es sorprendente que haya algunas consideraciones sobre el navegador.

Un iframe es un marco en línea, un marco que, si bien contiene una página completamente separada con su propia URL, se coloca dentro de otra página HTML. Esto ofrece muy buenas posibilidades en el diseño web. El problema es acceder al iframe, por ejemplo, para cargar una nueva página en él. Esta página explica cómo hacerlo.

Marco u objeto?

La pregunta fundamental es si el iframe se ve como un marco o como un objeto.

  • Como se explica en la Introducción a las páginas de marcos , si usa marcos, el navegador crea una jerarquía de marcos para usted (top.frames[1].frames[2] y tal). ¿Encaja el iframe en esta jerarquía de cuadros?
  • ¿O el navegador ve un iframe como un objeto más, un objeto que tiene una propiedad src? En ese caso, tenemos que usar una llamada DOM estándar (como document.getElementById('theiframe'))acceder a ella. En general, los navegadores permiten ambas vistas en iframes 'reales' (codificados), pero no se puede acceder a los iframes generados como marcos.

NOMBRE atributo

La regla más importante es dar a cualquier iframe que cree un nameatributo, incluso si también usa un id.

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

La mayoría de los navegadores necesitan el nameatributo para que el iframe forme parte de la jerarquía de cuadros. Algunos navegadores (especialmente Mozilla) necesitan idque el iframe sea accesible como un objeto. Al asignar ambos atributos al iframe, mantiene sus opciones abiertas. Pero namees mucho más importante que id.

Acceso

O accede al iframe como un objeto y lo cambia srco accede al iframe como un marco y lo cambia location.href.

document.getElementById ('iframe_id'). src = 'newpage.html'; marcos ['iframe_name']. location.href = 'newpage.html'; La sintaxis del marco es ligeramente preferible porque Opera 6 lo admite pero no la sintaxis del objeto.

Accediendo al iframe

Por lo tanto, para una experiencia completa entre navegadores, debe asignarle un nombre al iframe y usar el

frames['testiframe'].location.href

sintaxis. Hasta donde yo sé, esto siempre funciona.

Accediendo al documento

Acceder al documento dentro del iframe es bastante simple, siempre que use el nameatributo. Para contar el número de enlaces en el documento en el iframe, haga frames['testiframe'].document.links.length.

Iframes generados

Sin embargo, cuando genera un iframe a través del W3C DOM, el iframe no se ingresa inmediatamente en la framesmatriz, y elframes['testiframe'].location.href sintaxis no funcionará de inmediato. El navegador necesita un poco de tiempo antes de que el iframe aparezca en la matriz, tiempo durante el cual no se puede ejecutar ningún script.

los document.getElementById('testiframe').src sintaxis funciona bien en todas las circunstancias.

El targetatributo de un enlace tampoco funciona con los iframes generados, excepto en Opera, aunque di mi iframe generado tanto a namecomo aid .

La falta de targetsoporte significa que debe usar JavaScript para cambiar el contenido de un iframe generado, pero dado que necesita JavaScript de todos modos para generarlo en primer lugar, no veo esto como un gran problema.

Tamaño del texto en iframes

Un curioso solo error del Explorer 6:

Cuando cambia el tamaño del texto a través del menú Ver, los tamaños de texto en iframes se cambian correctamente. Sin embargo, este navegador no cambia los saltos de línea en el texto original, por lo que parte del texto puede volverse invisible o pueden ocurrir saltos de línea mientras la línea aún puede contener otra palabra.

activout.se
fuente
55
Ese enlace parece estar roto.
zaphod
1
Y para empeorar las cosas, no puedo encontrar la página relacionada en ninguna parte de quirksmode.
stricjux
7

Encontré una solución bastante elegante.

Como dijiste, es bastante fácil ejecutar el código ubicado en el documento principal. Y esa es la base de mi código, hacer todo lo contrario.

Cuando se carga mi iframe, llamo a una función ubicada en el documento principal, pasando como argumento una referencia a una función local, ubicada en el documento del iframe. El documento padre ahora tiene acceso directo a la función del iframe a través de esta referencia.

Ejemplo:

En el padre:

function tunnel(fn) {
    fn();
}

En el iframe:

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

parent.tunnel(myFunction);

Cuando se carga el iframe, llamará a parent.tunnel (YourFunctionReference), que ejecutará la función recibida en el parámetro.

Así de simple, sin tener que lidiar con todos los métodos no estándar de los distintos navegadores.

Julien L
fuente
Hola, ¿puedes confirmar esto "así de simple, sin tener que lidiar con todos los métodos no estándar de los distintos navegadores"?
Milche Patern
1
Utilizo este método en varios proyectos, parece funcionar muy bien. No he probado personalmente todos los navegadores en circulación, pero tampoco recibí un informe de error.
Julien L
Funciona como un encanto, IE 11, Chrome ~ 50.
Augustas
1
Supongo que eso no es dominio cruzado ... ¿o sí?
Tushar Shukla
@TusharShukla Lamentablemente no, no lo es.
jeff-h
6

Algunas de estas respuestas no abordan el problema de CORS, o no hacen obvio dónde coloca los fragmentos de código para hacer posible la comunicación.

Aquí hay un ejemplo concreto. Digamos que quiero hacer clic en un botón en la página principal y que haga algo dentro del iframe. Así es como lo haría.

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();
}

Para ir en la otra dirección (haga clic en el botón dentro del iframe para llamar al método fuera del iframe) simplemente cambie el lugar donde vive el fragmento de código y cambie esto:

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

a esto:

window.parent.postMessage('foo','*')
Cibernético
fuente
4

Continuando con JoelAnair's respuesta :

Para mayor robustez, use lo siguiente:

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

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

Funcionó como encanto :)

Nitin Bansal
fuente
3

Siguiendo la respuesta de Nitin Bansal

y para aún más robustez:

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;
}

y

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

var frame_win = getIframeWindow(el);

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

Las mismas cosas pero de una manera un poco más fácil será Cómo actualizar la página principal desde la página dentro del iframe . Simplemente llame a la función de la página principal para invocar la función de JavaScript para volver a cargar la página:

window.location.reload();

O haga esto directamente desde la página en iframe:

window.parent.location.reload();

Ambas obras.

sangam
fuente
1

Intenta solo parent.myfunction()

Saeid Zebardast
fuente
@Saeid El OP quiere llamar a una función en un iframe desde la ventana principal, no al revés.
Jean-François Beauchamp
1

Si desea invocar la función JavaScript en el elemento primario desde el iframe generado por otra función, por ejemplo, shadowbox o lightbox.

Debe intentar hacer uso del windowobjeto e invocar la función padre:

window.parent.targetFunction();
Santhanakumar
fuente
Creo que el OP está tratando de ir en la otra dirección
CommandZ
-3

Use el siguiente para llamar a la función de un marco en la página principal

parent.document.getElementById('frameid').contentWindow.somefunction()
Arenoso
fuente