jQuery / JavaScript: acceder al contenido de un iframe

792

Me gustaría manipular el HTML dentro de un iframe usando jQuery.

Pensé que sería capaz de hacer esto estableciendo el contexto de la función jQuery para que sea el documento del iframe, algo así como:

$(function(){ //document ready
    $('some selector', frames['nameOfMyIframe'].document).doStuff()
});

Sin embargo, esto no parece funcionar. Un poco de inspección me muestra que las variables frames['nameOfMyIframe']están a undefinedmenos que espere un momento a que se cargue el iframe. Sin embargo, cuando se carga el iframe, las variables no son accesibles (obtengo permission deniederrores de tipo).

¿Alguien sabe de una solución a esto?

rz.
fuente
3
¿Qué contiene el iFrame? ¿Está su src configurado en otro dominio?
James
si se trata de otro dominio, ¿hay alguna manera de acceder a su contenido o registrar un evento
adardesign
34
No, porque eso sería una secuencia de comandos entre sitios, lo cual está prohibido por razones de seguridad. Mi solución fue usar un proxy: alimentar el HTML en el IFRAME textualmente a través de mi propio sitio para que ya no sea entre sitios desde la perspectiva del navegador.
reinierpost
Es más un navegador cruzado para usar .contentWindow.documentque .documenten el iframeelemento. Sugeriré el cambio anterior.
Alan H.
1
una forma es extensiones de cromo
Muhammad Umer

Respuestas:

384

Creo que lo que está haciendo está sujeto a la misma política de origen . Esta debería ser la razón por la que obtiene errores de tipo de permiso denegado .

Onur Bebin
fuente
66
@Pacerier La mejor opción es representar el contenido del iframe en su sitio, si puede ...
Tracker1
10
@ Tracker1: ¿Puede sugerir algún marco / api / patrón de diseño para implementar esta solución proxy? ¿Algún enlace al ejemplo o tutorial, etc.? Intenté buscar pero no pude encontrar ninguno.
Umer Hayat
3
En este caso, quiere decir usar el servidor http que sirve la página de su dominio como proxy: solicite el contenido del sitio de terceros y reenvíelo en la respuesta http al cliente. Como probablemente pueda adivinar, afecta rápidamente la capacidad de respuesta de su sitio, ya que las solicitudes paralelas anteriores se ejecutan en serie con su servidor como un posible cuello de botella.
rutherford
1
@Pacerier Potencialmente, cualquiera de los métodos en la respuesta aceptada aquí: stackoverflow.com/questions/3076414/… u otros (como usar su propio dominio como proxy), dependiendo de lo que quiera lograr.
Mark Amery el
44
Para el registro, acabo de configurar algo similar: si no desea que el resultado sea increíblemente lento, simplemente configure su servidor web para redirigir directamente las solicitudes de activos (CSS / JS / images) al sitio web original, por lo que su proxy solo gestiona solicitudes HTML. Estoy navegando por un sitio distante en localhost ahora y es absolutamente transparente :)
neemzy
985

Si el <iframe>es del mismo dominio, los elementos son fácilmente accesibles como

$("#iFrame").contents().find("#someDiv").removeClass("hidden");

Referencia

Yasir Laghari
fuente
157
Conocer la misma política de origen es excelente, pero esta es la respuesta que hace lo que el OP quería. ¿Por qué la otra respuesta fue elegida como la correcta?
Jason Swett
2
Tienes que usar un proxy para obtener el HTML en tu origen. Por ejemplo, johnchapman.name/…
mhenry1384
77
@JasonSwett, supongo que el problema de OP es de hecho el same origin policy, en cuyo caso esta respuesta no es una solución para él.
ANeves el
3
@fizzbuzz Luego obtienes el IFrame de la misma manera que obtendrías cualquier otro contenido sin identificación con jQuery: seleccionas la etiqueta <IFrame> apropiada con CSS, usando nombres de clase y valores de atributos para reducirlo si es necesario.
Auspex
2
No existe un método llamado contenido para el iframe.
Renan
88

Podrías usar el .contents()método de jQuery.

El .contents()método también se puede usar para obtener el documento de contenido de un iframe, si el iframe está en el mismo dominio que la página principal.

$(document).ready(function(){
    $('#frameID').load(function(){
        $('#frameID').contents().find('body').html('Hey, i`ve changed content of <body>! Yay!!!');
    });
});
davryusha
fuente
2
necesito obtener el contenido del marco i para no configurar el cuerpo ..... Cuando hago lo anterior, no funciona, siempre devuelve el cuerpo vacío
George Hanna
3
@DarkoZ Um, en realidad esta respuesta vino primero, así que, en todo caso, la otra respuesta fue la copia
michaelpri
@DarkoZ: dejar el comentario original no me hizo avergonzarlo, pero perdí como 30 segundos tratando de entender un problema :) Así que eliminar esta discusión completa sobre una respuesta plagiante podría ser lo mejor.
Dan Dascalescu
comentarios eliminados para evitar confusiones. disculpas
Darko Z
43

Si el iframe src es de otro dominio, aún puede hacerlo. Debe leer la página externa en PHP y hacerla eco de su dominio. Me gusta esto:

iframe_page.php

<?php
    $URL = "http://external.com";

    $domain = file_get_contents($URL);

    echo $domain;
?>

Entonces algo como esto:

display_page.html

<html>
<head>
  <title>Test</title>
 </head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>

<script>

$(document).ready(function(){   
    cleanit = setInterval ( "cleaning()", 500 );
});

function cleaning(){
    if($('#frametest').contents().find('.selector').html() == "somthing"){
        clearInterval(cleanit);
        $('#selector').contents().find('.Link').html('ideate tech');
    }
}

</script>

<body>
<iframe name="frametest" id="frametest" src="http://yourdomain.com/iframe_page.php" ></iframe>
</body>
</html>

Lo anterior es un ejemplo de cómo editar una página externa a través de un iframe sin el acceso denegado, etc.

basysmith
fuente
12
Por supuesto, debido a que su servidor obtiene la página remota, no el navegador del usuario, no se enviarán cookies a la página remota. YMMV.
Mark Fowler el
1
@ Mark: puede enviar fácilmente cookies, datos publicados, encabezados HTTP y otras cosas si lo implementa con la extensión curl. php.net/manual/en/book.curl.php
geon
2
@geon: pero el navegador no enviará cookies para el dominio extranjero a su script PHP
2011
66
@basysmith Tenga en cuenta: hay una razón por la cual same origin policyexiste, y la propuesta de esta respuesta no es inmune a ella.
ANeves el
1
¿Es ilegal de alguna manera hacer esto?
Tallboy
32

Encuentro esta manera más limpia:

var $iframe = $("#iframeID").contents();
$iframe.find('selector');
zupa
fuente
Salvé mi trasero: me estaba volviendo loco, usando "$ (" # iframe "). Contenido (). Find ()" no estaba funcionando por alguna razón ... lo mismo sobre dos líneas lo hizo. No sé por qué, pero funciona.
neminem
30

Utilizar

iframe.contentWindow.document

en vez de

iframe.contentDocument
usuario
fuente
Gran respuesta. Esto funciona para iFrames en otros dominios. Probé esto en la consola en Safari y Firefox.
Aneil Mallavarapu
12
Creo que solo funcionará para otros dominios si lo está haciendo desde la consola. Desde un script, no se permitirá el acceso.
yincrash
Esta debería ser la respuesta aceptada. Me salvó la vida y funciona cuando el iframe es de un dominio diferente; aunque como se mencionó, solo en la consola.
silkfire
para mí iframe.contentWindow.document no siempre funciona. y cuando no es así, descubrí que $ (elem) .contents (). get (0) sí
elewinso
puede elegir el "documento raíz" en la consola del navegador. Cuando selecciona "arriba" no funcionará, como un acceso de origen cruzado. Si elige acceder en nombre del documento iframe, por supuesto, le dará acceso. Solo porque no eres un navegador, sino el propietario del navegador.
Sergei Kovalenko
26

Debe adjuntar un evento al controlador de carga de un iframe y ejecutar el js allí, para asegurarse de que el iframe haya terminado de cargarse antes de acceder a él.

$().ready(function () {
    $("#iframeID").ready(function () { //The function below executes once the iframe has finished loading
        $('some selector', frames['nameOfMyIframe'].document).doStuff();
    });
};

Lo anterior resolverá el problema de "aún no cargado", pero con respecto a los permisos, si está cargando una página en el iframe que es de un dominio diferente, no podrá acceder a ella debido a restricciones de seguridad.

Andreas Grech
fuente
Esta es una buena idea, de hecho, lo estaba intentando como respondiste. Sin embargo, no funciona con el permiso denegado (aborda mi necesidad de esperar antes de comenzar a acceder al material del iframe)
rz.
en realidad ... no importa, parece que la espera aún es necesaria incluso al hacer esto.
rz.
La función de listo funciona. Sin embargo, parece que no espera a que el contenido del iframe termine de cargarse, solo al documento principal, incluso cuando se invoca en el contenido del iframe. Me imagino que también se debe a la misma política de origen.
rz.
3
Un par de notas sobre este código: debe usar iframe.contentDocument en lugar de .document, y debe usar .load () en lugar de .ready () para evitar la espera. (No perfecto, pero mejor)
Rodrigo Queiro
21

Puede usar window.postMessage para llamar a una función entre la página y su iframe (dominio cruzado o no).

Documentación

page.html

<!DOCTYPE html>
<html>
<head>
    <title>Page with an iframe</title>
    <meta charset="UTF-8" />
    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
    <script>
    var Page = {
        id:'page',
        variable:'This is the page.'
    };

    $(window).on('message', function(e) {
        var event = e.originalEvent;
        if(window.console) {
            console.log(event);
        }
        alert(event.origin + '\n' + event.data);
    });
    function iframeReady(iframe) {
        if(iframe.contentWindow.postMessage) {
            iframe.contentWindow.postMessage('Hello ' + Page.id, '*');
        }
    }
    </script>
</head>
<body>
    <h1>Page with an iframe</h1>
    <iframe src="iframe.html" onload="iframeReady(this);"></iframe>
</body>
</html>

iframe.html

<!DOCTYPE html>
<html>
<head>
    <title>iframe</title>
    <meta charset="UTF-8" />
    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
    <script>
    var Page = {
        id:'iframe',
        variable:'The iframe.'
    };

    $(window).on('message', function(e) {
        var event = e.originalEvent;
        if(window.console) {
            console.log(event);
        }
        alert(event.origin + '\n' + event.data);
    });
    $(window).on('load', function() {
        if(window.parent.postMessage) {
            window.parent.postMessage('Hello ' + Page.id, '*');
        }
    });
    </script>
</head>
<body>
    <h1>iframe</h1>
    <p>It's the iframe.</p>
</body>
</html>
B. Aselina
fuente
4

Prefiero usar otra variante para acceder. Desde el padre puede tener acceso a la variable en el iframe secundario. $también es una variable y puede recibir acceso a su llamada window.iframe_id.$

Por ejemplo, window.view.$('div').hide()- ocultar todos los divs en iframe con id 'view'

Pero, no funciona en FF. Para una mejor compatibilidad deberías usar

$('#iframe_id')[0].contentWindow.$

Evgeny Karpov
fuente
2

¿Has probado el clásico, esperando a que se complete la carga con la función integrada de jQuery?

$(document).ready(function() {
    $('some selector', frames['nameOfMyIframe'].document).doStuff()
} );

K

Khb
fuente
2
Si. La función ready comienza a ejecutarse cuando se carga el marco principal, no cuando se carga el iframe. Sin embargo, la espera parece ser una pequeña parte del problema. Creo que tiene que ver con la seguridad entre dominios.
rz.
2

Creo un código de muestra. Ahora puede comprender fácilmente desde diferentes dominios que no puede acceder al contenido del iframe. El mismo dominio podemos acceder al contenido del iframe

Te comparto mi código. Ejecuta este código, verifica la consola. Imprimo la imagen src en la consola. Hay cuatro iframe, dos iframe que provienen del mismo dominio y otros dos de otro dominio (tercero). Puede ver dos imágenes src ( https://www.google.com/logos/doodles/2015/googles-new-logo -5078286822539264.3-hp2x.gif

y

https://www.google.com/logos/doodles/2015/arbor-day-2015-brazil-5154560611975168-hp2x.gif ) en la consola y también puede ver dos errores de permiso (2 Error: permiso denegado para acceder al documento de la propiedad '

... irstChild)}, contenido: función (a) {return m.nodeName (a, "iframe")? a.contentDocument ...

) que proviene de un iframe de terceros.

<body id="page-top" data-spy="scroll" data-target=".navbar-fixed-top">
<p>iframe from same domain</p>
  <iframe frameborder="0" scrolling="no" width="500" height="500"
   src="iframe.html" name="imgbox" class="iView">

</iframe>
<p>iframe from same domain</p>
<iframe frameborder="0" scrolling="no" width="500" height="500"
   src="iframe2.html" name="imgbox" class="iView1">

</iframe>
<p>iframe from different  domain</p>
 <iframe frameborder="0" scrolling="no" width="500" height="500"
   src="https://www.google.com/logos/doodles/2015/googles-new-logo-5078286822539264.3-hp2x.gif" name="imgbox" class="iView2">

</iframe>

<p>iframe from different  domain</p>
 <iframe frameborder="0" scrolling="no" width="500" height="500"
   src="http://d1rmo5dfr7fx8e.cloudfront.net/" name="imgbox" class="iView3">

</iframe>

<script type='text/javascript'>


$(document).ready(function(){
    setTimeout(function(){


        var src = $('.iView').contents().find(".shrinkToFit").attr('src');
    console.log(src);
         }, 2000);


    setTimeout(function(){


        var src = $('.iView1').contents().find(".shrinkToFit").attr('src');
    console.log(src);
         }, 3000);


    setTimeout(function(){


        var src = $('.iView2').contents().find(".shrinkToFit").attr('src');
    console.log(src);
         }, 3000);

         setTimeout(function(){


        var src = $('.iView3').contents().find("img").attr('src');
    console.log(src);
         }, 3000);


    })


</script>
</body>
Zisu
fuente
1

Terminé aquí buscando obtener el contenido de un iframe sin jquery, así que para cualquiera que esté buscando eso, es solo esto:

document.querySelector('iframe[name=iframename]').contentDocument
Jeff Davis
fuente
1

Esta solución funciona igual que iFrame. He creado un script PHP que puede obtener todos los contenidos del otro sitio web, y la parte más importante es que puede aplicar fácilmente su jQuery personalizado a ese contenido externo. Consulte el siguiente script que puede obtener todos los contenidos del otro sitio web y luego puede aplicar su cusom jQuery / JS también. Este contenido se puede usar en cualquier lugar, dentro de cualquier elemento o página.

<div id='myframe'>

  <?php 
   /* 
    Use below function to display final HTML inside this div
   */

   //Display Frame
   echo displayFrame(); 
  ?>

</div>

<?php

/* 
  Function to display frame from another domain 
*/

function displayFrame()
{
  $webUrl = 'http://[external-web-domain.com]/';

  //Get HTML from the URL
  $content = file_get_contents($webUrl);

  //Add custom JS to returned HTML content
  $customJS = "
  <script>

      /* Here I am writing a sample jQuery to hide the navigation menu
         You can write your own jQuery for this content
      */
    //Hide Navigation bar
    jQuery(\".navbar.navbar-default\").hide();

  </script>";

  //Append Custom JS with HTML
  $html = $content . $customJS;

  //Return customized HTML
  return $html;
}
Ahsan Horani
fuente
0

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 frame_win = getIframeWindow( frames['nameOfMyIframe'] );

if (frame_win) {
  $(frame_win.contentDocument || frame_win.document).find('some selector').doStuff();
  ...
}
...
Dominique Fortin
fuente