¿Devolución de llamada de Javascript cuando IFRAME ha terminado de cargar?

147

Necesito ejecutar una devolución de llamada cuando un IFRAME ha terminado de cargarse. No tengo control sobre el contenido en el IFRAME, por lo que no puedo activar la devolución de llamada desde allí.

Este IFRAME se crea mediante programación y necesito pasar sus datos como una variable en la devolución de llamada, así como destruir el iframe.

¿Algunas ideas?

EDITAR:

Esto es lo que tengo ahora:

function xssRequest(url, callback)
{
    var iFrameObj = document.createElement('IFRAME');
    iFrameObj.src = url;            
    document.body.appendChild(iFrameObj);   

    $(iFrameObj).load(function() 
    {
        document.body.removeChild(iFrameObj);
        callback(iFrameObj.innerHTML);
    });
}

Esta devolución de llamada antes de que se cargue el iFrame, por lo que la devolución de llamada no tiene datos devueltos.

FlySwat
fuente
1
Creo que no desea adjuntar el controlador de eventos en el iframe, pero es la ventana de contenido.
bobwienholt
1
El problema es la solicitud de dominio cruzado. No puede hacerlo si el iframe es de otro dominio
Victor
En realidad, hay un evento de carga en el objeto de iframe que se dispara cada vez que el iframe termina de cargar un documento; de lo contrario, deberá conectar a la ventana después de cada carga
Juan Mendes
Vea mi respuesta aquí: stackoverflow.com/a/36155560/3894981
amigo

Respuestas:

49

En primer lugar, siguiendo el nombre de la función xssRequest , parece que está intentando una solicitud entre sitios, lo que si es correcto, no podrá leer el contenido del iframe.

Por otro lado, si la URL del iframe está en su dominio, puede acceder al cuerpo, pero he descubierto que si uso un tiempo de espera para eliminar el iframe, la devolución de llamada funciona bien:

// possibly excessive use of jQuery - but I've got a live working example in production
$('#myUniqueID').load(function () {
  if (typeof callback == 'function') {
    callback($('body', this.contentWindow.document).html());
  }
  setTimeout(function () {$('#frameId').remove();}, 50);
});
Remy Sharp
fuente
44
podría reemplazar $('body', this.contentWindow.document).html()conthis.contentDocument.body.outerHTML
Sam Soffes
12
¿Alguna idea de cómo hacer esto sin jQuery?
devios1
66
A partir de jQuery 3.0, loadse ha ido. Por favor, use .on('load', function() { ... })en su lugar.
Doppelganger
36

Estoy usando jQuery y sorprendentemente esto parece cargarse, ya que acabo de probar y cargar una página pesada y no recibí la alerta durante unos segundos hasta que vi la carga del iframe:

$('#the_iframe').load(function(){
    alert('loaded!');
});

Entonces, si no desea usar jQuery, eche un vistazo a su código fuente y vea si esta función se comporta de manera diferente con los elementos DOM del iframe, lo veré yo mismo más tarde, ya que estoy interesado y publicaré aquí. También solo probé en el último cromo.

Neo
fuente
Noté que creó el elemento DOM con javascript puro y luego pasó esa variable a jQuery como selector, ¿tal vez es por eso que su código no funciona?
Neo
12
A partir de jQuery 3.0, loadse ha ido. Por favor, use .on('load', function() { ... })en su lugar.
Doppelganger
8

El innerHTML de su iframe está en blanco porque su etiqueta de iframe no rodea ningún contenido en el documento principal. Para obtener el contenido de la página a la que hace referencia el atributo src del iframe, debe acceder a la propiedad contentDocument del iframe. Sin embargo, se lanzará una excepción si el src es de un dominio diferente. Esta es una característica de seguridad que le impide ejecutar JavaScript arbitrario en la página de otra persona, lo que crearía una vulnerabilidad de secuencias de comandos entre sitios. Aquí hay un código de ejemplo que ilustra de lo que estoy hablando:

<script src="http://prototypejs.org/assets/2009/8/31/prototype.js" type="text/javascript"></script>

<h1>Parent</h1>

<script type="text/javascript">
function on_load(iframe) {
  try {
    // Displays the first 50 chars in the innerHTML of the
    // body of the page that the iframe is showing.
    // EDIT 2012-04-17: for wider support, fallback to contentWindow.document
    var doc = iframe.contentDocument || iframe.contentWindow.document;
    alert(doc.body.innerHTML.substring(0, 50));
  } catch (e) {
    // This can happen if the src of the iframe is
    // on another domain
    alert('exception: ' + e);
  }
}
</script>
<iframe id="child" src="iframe_content.html" onload="on_load(this)"></iframe>

Para avanzar en el ejemplo, intente usar esto como contenido del iframe:

<h1>Child</h1>

<a href="http://www.google.com/">Google</a>

<p>Use the preceeding link to change the src of the iframe
to see what happens when the src domain is different from
that of the parent page</p>
allyourcode
fuente
1
La propiedad contentDocument no es compatible con IE 7 y tal vez más IE también, la forma de tratar con IE es window ['iframeid']. document o lo que es exactamente el mismo window.frames ['iframeid']. enlace de prueba de documento aquí desarrollador. mozilla.org/en/…
Olga
7

Tuve que hacer esto en los casos en que documentos como documentos de texto y archivos PDF se transmitían al iframe y encontré una solución que funciona bastante bien. La clave es manejar el onreadystatechangedevento en el iframe.

Digamos que el nombre de su marco es "myIframe". Primero en algún lugar de su inicio de código (lo hago en línea en cualquier lugar después del iframe) agregue algo como esto para registrar el controlador de eventos:

document.getElementById('myIframe').onreadystatechange = MyIframeReadyStateChanged;

No pude usar un atributo onreadystatechage en el iframe, no recuerdo por qué, pero la aplicación tuvo que funcionar en IE 7 y Safari 3, por lo que puede haber sido un factor.

Aquí hay un ejemplo de cómo obtener el estado completo:

function MyIframeReadyStateChanged()
{
    if(document.getElementById('myIframe').readyState == 'complete')
    {
        // Do your complete stuff here.
    }
}
Ryan Cook
fuente
6

Quería ocultar la división del spinner en espera cuando el contenido del marco i está completamente cargado en IE, probé literalmente todas las soluciones mencionadas en Stackoverflow.Com, pero nada funcionó como quería.

Entonces tuve una idea, que cuando el contenido del marco i está completamente cargado, el evento de carga $ (Window) podría ser activado. Y eso exactamente lo que pasó. Entonces, escribí este pequeño guión y trabajé como magia:

     $(window).load(function () {
     //alert("Done window ready ");
     var lblWait = document.getElementById("lblWait");
     if (lblWait != null ) {
         lblWait.style.visibility = "false";
         document.getElementById("divWait").style.display = "none";
     }
 });

Espero que esto ayude.

Nada N. Hantouli
fuente
Esta es una solución simple y funciona bien si desea que su JavaScript se ejecute DESPUÉS de que se cargue toda la página. Esto significa que primero verá la página completamente cargada, sin efecto js, ​​y después de tal vez un segundo o dos (dependiendo de la carga), cualquier cambio creado por el JavaScript ocurrirá en ese momento. Personalmente intenté cambiar el tamaño de mi iframe, y funciona si no prestas atención a la pantalla durante los primeros tres segundos después de hacer clic en una página, pero de lo contrario esto (como fue en mi caso) puede aparecer como un mosaico para el código.
ampersands
1
gracias por su comentario, esta solución funcionó muy bien en mi caso, ya que quería que el iframe se cargara después de cargar la página.
Nada N. Hantouli
2

Tuve un problema similar al tuyo. Lo que hice es usar algo llamado jQuery. Lo que luego haces en el código JavaScript es esto:

$(function(){ //this is regular jQuery code. It waits for the dom to load fully the first time you open the page.

    $("#myIframeId").load(function(){
       callback($("#myIframeId").html());
       $("#myIframeId").remove();

    });

});

Parece que eliminas tu iFrame antes de tomar el html. Ahora, veo un problema con eso: p

Espero que esto ayude :).

Automatico
fuente
1

Tengo un código similar en mis proyectos que funciona bien. Adaptando mi código a su función, una solución podría ser la siguiente:

function xssRequest(url, callback)
{
    var iFrameObj = document.createElement('IFRAME');
    iFrameObj.id = 'myUniqueID';
    document.body.appendChild(iFrameObj);       
    iFrameObj.src = url;                        

    $(iFrameObj).load(function() 
    {
        callback(window['myUniqueID'].document.body.innerHTML);
        document.body.removeChild(iFrameObj);
    });
}

Tal vez tenga un innerHTML vacío porque (una o ambas causas): 1. debe usarlo contra el elemento del cuerpo 2. ha eliminado el iframe del DOM de su página

Pier Luigi
fuente
1

Creo que el evento de carga es correcto. Lo que no es correcto es la forma en que utiliza para recuperar el contenido del dominio de contenido de iframe.

Lo que necesita es el html de la página cargada en el iframe, no el html del objeto iframe.

Lo que debe hacer es acceder al documento de contenido con iFrameObj.contentDocument. Esto devuelve el dom de la página cargada dentro del iframe, si está en el mismo dominio de la página actual.

Retiré el contenido antes de eliminar el iframe.

Lo he probado en Firefox y Opera.

Entonces creo que puede recuperar sus datos con $(childDom).html()o$(childDom).find('some selector') ...


fuente
0

Tuve exactamente el mismo problema en el pasado y la única forma de solucionarlo fue agregar la devolución de llamada a la página del iframe. Por supuesto, eso solo funciona cuando tienes control sobre el contenido del iframe.

roryf
fuente
22
No lo rechacé, pero imagino que fue rechazado porque sugirió exactamente lo que dijo que no puede hacer: dijo específicamente: "No tengo control sobre el contenido del IFRAME, así que puedo" t dispare la devolución de llamada desde allí ".
Dave Haynes
-1

Usar onloadattrbute resolverá tu problema.

Aquí hay un ejemplo.

function a() {
alert("Your iframe has been loaded");
}
<iframe src="https://stackoverflow.com" onload="a()"></iframe>

¿Es esto lo que quieres?

Haga clic aquí para más información.


fuente