Cambiar el tamaño de un iframe basado en el contenido


498

Estoy trabajando en una aplicación similar a iGoogle. El contenido de otras aplicaciones (en otros dominios) se muestra usando iframes.

¿Cómo cambio el tamaño de los iframes para que se ajuste a la altura del contenido de los iframes?

He intentado descifrar el javascript que usa Google, pero está ofuscado, y la búsqueda en la web ha sido infructuosa hasta ahora.

Actualización: tenga en cuenta que el contenido se carga desde otros dominios, por lo que se aplica la política del mismo origen .

Respuestas:


584

Tuvimos este tipo de problema, pero un poco inverso a su situación: estábamos proporcionando el contenido iframed a sitios en otros dominios, por lo que la misma política de origen también fue un problema. Después de pasar muchas horas buscando en Google, finalmente encontramos una solución (algo ...) viable, que puede adaptar a sus necesidades.

Hay una forma de evitar la misma política de origen, pero requiere cambios tanto en el contenido iframed como en la página de trama, por lo que si no puede solicitar cambios en ambos lados, este método no será muy útil para usted, Me temo que.

Hay una peculiaridad del navegador que nos permite eludir la misma política de origen: JavaScript puede comunicarse con páginas en su propio dominio o con páginas que tiene iframed, pero nunca páginas en las que está enmarcado, por ejemplo, si tiene:

 www.foo.com/home.html, which iframes
 |-> www.bar.net/framed.html, which iframes
     |-> www.foo.com/helper.html

luego home.htmlpuede comunicarse con framed.html(iframed) y helper.html(mismo dominio).

 Communication options for each page:
 +-------------------------+-----------+-------------+-------------+
 |                         | home.html | framed.html | helper.html |
 +-------------------------+-----------+-------------+-------------+
 | www.foo.com/home.html   |    N/A    |     YES     |     YES     |
 | www.bar.net/framed.html |    NO     |     N/A     |     YES     |
 | www.foo.com/helper.html |    YES    |     YES     |     N/A     |
 +-------------------------+-----------+-------------+-------------+

framed.htmlpuede enviar mensajes a helper.html(iframed) pero no home.html (el niño no puede comunicarse entre dominios con el padre).

La clave aquí es que helper.htmlpuede recibir mensajes framed.htmly también puede comunicarse con ellos home.html.

Entonces, esencialmente, cuando se framed.htmlcarga, calcula su propia altura, le dice helper.html, a quién le pasa el mensaje home.html, que luego puede cambiar el tamaño del iframe en el que se framed.htmlencuentra.

La forma más sencilla de encontrar mensajes de framed.htmla helper.htmlfue a través de un argumento de URL. Para hacer esto, framed.htmltiene un iframe con src=''especificado. Cuando se onloaddispara, evalúa su propia altura y establece el src del iframe en este punto enhelper.html?height=N

¡Aquí hay una explicación de cómo Facebook lo maneja, que puede ser un poco más claro que el mío anterior!


Código

En www.foo.com/home.html, se requiere el siguiente código javascript (esto se puede cargar desde un archivo .js en cualquier dominio, por cierto ...):

<script>
  // Resize iframe to full height
  function resizeIframe(height)
  {
    // "+60" is a general rule of thumb to allow for differences in
    // IE & and FF height reporting, can be adjusted as required..
    document.getElementById('frame_name_here').height = parseInt(height)+60;
  }
</script>
<iframe id='frame_name_here' src='http://www.bar.net/framed.html'></iframe>

En www.bar.net/framed.html:

<body onload="iframeResizePipe()">
<iframe id="helpframe" src='' height='0' width='0' frameborder='0'></iframe>

<script type="text/javascript">
  function iframeResizePipe()
  {
     // What's the page height?
     var height = document.body.scrollHeight;

     // Going to 'pipe' the data to the parent through the helpframe..
     var pipe = document.getElementById('helpframe');

     // Cachebuster a precaution here to stop browser caching interfering
     pipe.src = 'http://www.foo.com/helper.html?height='+height+'&cacheb='+Math.random();

  }
</script>

Contenido de www.foo.com/helper.html:

<html> 
<!-- 
This page is on the same domain as the parent, so can
communicate with it to order the iframe window resizing
to fit the content 
--> 
  <body onload="parentIframeResize()"> 
    <script> 
      // Tell the parent iframe what height the iframe needs to be
      function parentIframeResize()
      {
         var height = getParam('height');
         // This works as our parent's parent is on our domain..
         parent.parent.resizeIframe(height);
      }

      // Helper function, parse param from request string
      function getParam( name )
      {
        name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
        var regexS = "[\\?&]"+name+"=([^&#]*)";
        var regex = new RegExp( regexS );
        var results = regex.exec( window.location.href );
        if( results == null )
          return "";
        else
          return results[1];
      }
    </script> 
  </body> 
</html>

77
realmente lleno de perspicacia. Me preguntaba por qué igoogle.com tenía un archivo helper.html. Gracias
IEnumerator

99
¡Genial gracias! Hice un par de adiciones: use jQuery para obtener la altura del cuerpo en framed.html (problema de FF, el cuerpo siguió creciendo): var height = $ (document.body) .height (); Usó un evento de carga corporal para crear el marco en home.html similar a su enfoque en framed.html. Direcciones que no actualizan el marco al actualizar en FF y Safari.
Abdullah Jibaly

55
¡Genio! Además de las adiciones de Abdullah: en lugar de configurar la parentIframeResize()carga del cuerpo para llamar , utilicé JQuery para capturar tanto la carga de la página como cualquier evento de cambio de tamaño: $(document).ready(iframeResizePipe); $(window).resize(iframeResizePipe);esto me permite configurar el iframe width="100%"y si el ancho de la ventana cambia para ajustar el texto o algo así, el marco interno reconocerá que necesita ser redimensionado.
StriplingWarrior

99
Sugiero dar al iframe id = frame_name_here un atributo de altura predeterminado como height = "2000". Esto significa que cuando la página carga el contenido enmarcado es visible. Cuando ocurre el cambio de tamaño no hay "parpadeo". El cambio de tamaño se está reduciendo / expandiendo debajo del pliegue de la página. Si conoce la altura máxima de su contenido iframed, use ese valor ... esto también resulta en una mejor experiencia noscript.
Chris Jacob

3
Siete años más tarde, sigue siendo la mejor solución entre navegadores y dominios cruzados que he encontrado. Bravo.
FurryWombat

81

Si no necesita manejar el contenido de iframe de un dominio diferente, pruebe este código, resolverá el problema por completo y es simple:

<script language="JavaScript">
<!--
function autoResize(id){
    var newheight;
    var newwidth;

    if(document.getElementById){
        newheight=document.getElementById(id).contentWindow.document .body.scrollHeight;
        newwidth=document.getElementById(id).contentWindow.document .body.scrollWidth;
    }

    document.getElementById(id).height= (newheight) + "px";
    document.getElementById(id).width= (newwidth) + "px";
}
//-->
</script>

<iframe src="usagelogs/default.aspx" width="100%" height="200px" id="iframe1" marginheight="0" frameborder="0" onLoad="autoResize('iframe1');"></iframe>

99
Puede ser más óptimo pasar el elemento en sí a la función para evitar todos los document.getElementById(id)mensajes que tiene y simplificar el código.
Muhd

1
Esto no funciona para mí ya que contentWindow.document.body.scrollHeight contiene 0 y no la altura real.
kroiz 01 de

44
Esta solución, como todas las otras que probé, funciona solo si la altura aumenta, si desde una página alta navegas a una más corta, la altura aún se establece de acuerdo con la altura de la primera. ¿Alguien encontró una solución para este problema?
Eugenio

66
@Eugenio y todos los demás, incluido yo, para solucionar este problema final: consulte stackoverflow.com/questions/3053072/... Antes de solicitar la altura del documento dentro del iframe, debe establecer la altura del objeto iframe en "auto". Tenga en cuenta que debe usar style.height no solo altura como en la respuesta anterior
Kyle el

77
No lo entiendo ... ¿comprueba la existencia de document.getElementById y luego lo llama fuera del área marcada de todos modos?
Legolas

41

https://developer.mozilla.org/en/DOM/window.postMessage

window.postMessage ()

window.postMessage es un método para habilitar de forma segura la comunicación de origen cruzado. Normalmente, los scripts en diferentes páginas solo pueden acceder entre sí si y solo si las páginas que los ejecutaron están en ubicaciones con el mismo protocolo (generalmente ambos http), número de puerto (80 es el valor predeterminado para http) y host (módulo documento.dominio establecido por ambas páginas al mismo valor). window.postMessage proporciona un mecanismo controlado para sortear esta restricción de manera segura cuando se usa correctamente.

Resumen

window.postMessage, cuando se llama, hace que se envíe un MessageEvent en la ventana de destino cuando se completa cualquier script pendiente que se debe ejecutar (por ejemplo, controladores de eventos restantes si se llama a window.postMessage desde un controlador de eventos, tiempos de espera pendientes previamente establecidos, etc. ) MessageEvent tiene el tipo de mensaje, una propiedad de datos que se establece en el valor de cadena del primer argumento proporcionado a window.postMessage, una propiedad de origen correspondiente al origen del documento principal en la ventana que llama a window.postMessage en la ventana de tiempo. Se llamó a postMessage y una propiedad de origen que es la ventana desde la que se llama a window.postMessage. (Otras propiedades estándar de los eventos están presentes con sus valores esperados).

La biblioteca iFrame-Resizer usa postMessage para mantener un iFrame dimensionado según su contenido, junto con MutationObserver para detectar cambios en el contenido y no depende de jQuery.

https://github.com/davidjbradshaw/iframe-resizer

jQuery: bondad de scripting entre dominios

http://benalman.com/projects/jquery-postmessage-plugin/

Tiene demostración de redimensionar ventana de iframe ...

http://benalman.com/code/projects/jquery-postmessage/examples/iframe/

Este artículo muestra cómo eliminar la dependencia de jQuery ... Plus tiene mucha información útil y enlaces a otras soluciones.

http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage/

Ejemplo de barebones ...

http://onlineaspect.com/uploads/postmessage/parent.html

Borrador de trabajo HTML 5 en window.postMessage

http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages

John Resig en mensajería cruzada

http://ejohn.org/blog/cross-window-messaging/


postMessage por sí solo no es un navegador cruzado, pero el complemento jQuery parece hacer un buen trabajo fingiendo cuando es necesario. El único problema real es la "basura" que se agrega a la barra de URL en los casos en que postMessage no es compatible.
Herms

2
postMessage parece ser X-Browser. Solo el IE debe tener errores o cosas que cuidar: 1. Comunicación solo entre marcos o iframes 2. Solo es capaz de publicar cadenas. Ver: caniuse.com/x-doc-messaging
Christian Kuetbach el

3
Hubiera sido agradable verte responder a la pregunta en lugar de explicarte sobre una tecnología de asistencia que ayuda con una solución ..
hitautodestruct

periscopedata está usando postMessage, si es lo suficientemente bueno para ellos, es lo suficientemente bueno para nosotros. Aquí están sus documentos: doc.periscopedata.com/docv2/embed-api-options
Yevgeniy Afanasyev

8

La forma más simple de usar jQuery:

$("iframe")
.attr({"scrolling": "no", "src":"http://www.someotherlink.com/"})
.load(function() {
    $(this).css("height", $(this).contents().height() + "px");
});

14
No cruza el dominio según lo solicitado.
willem

6

La solución en http://www.phinesolutions.com/use-jquery-to-adjust-the-iframe-height.html funciona muy bien (usa jQuery):

<script type=”text/javascript”>
  $(document).ready(function() {
    var theFrame = $(”#iFrameToAdjust”, parent.document.body);
    theFrame.height($(document.body).height() + 30);
  });
</script>

No sé si necesitas agregar 30 a la longitud ... 1 funcionó para mí.

FYI : Si ya tiene un atributo "height" en su iFrame, esto simplemente agrega style = "height: xxx". Esto podría no ser lo que quieres.


77
No dominio cruzado, no.
Volomike

Justo lo que necesitaba ya que no estoy haciendo cross-domain, +1 para html válido.
WildJoe

4

puede llegar un poco tarde, ya que todas las otras respuestas son anteriores :-) pero ... aquí está mi solución. Probado en FF real, Chrome y Safari 5.0.

css:

iframe {border:0; overflow:hidden;}

javascript:

$(document).ready(function(){
    $("iframe").load( function () {
        var c = (this.contentWindow || this.contentDocument);
        if (c.document) d = c.document;
        var ih = $(d).outerHeight();
        var iw = $(d).outerWidth();
        $(this).css({
            height: ih,
            width: iw
        });
    });
});

Espero que esto ayude a cualquiera.


@pashute: ¿te refieres a "dominio cruzado"?
Lucas

lo siento. Significa: No funciona multiplataforma ... Aquí hay una idea pública: gist.github.com/pashute/c4705ce7f767f50fdf56d0030ecf9192 Obtiene un error Rechazado para ejecutar. Cambiar el script para que tenga type = "text / javascript" no ayuda. Tampoco establecer el ancho y la altura del iframe (digamos 80%).
pashute

4

Finalmente encontré otra solución para enviar datos al sitio web principal desde iframe usando window.postMessage(message, targetOrigin); . Aquí explico cómo lo hice.

Sitio A = http://foo.com Sitio B = http://bar.com

El sitio B se está cargando dentro del sitio Un sitio web

El sitio web de SiteB tiene esta línea

window.parent.postMessage("Hello From IFrame", "*"); 

o

window.parent.postMessage("Hello From IFrame", "http://foo.com");

Entonces el sitio A tiene el siguiente código

// Here "addEventListener" is for standards-compliant web browsers and "attachEvent" is for IE Browsers.
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];


var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

// Listen to message from child IFrame window
eventer(messageEvent, function (e) {
   alert(e.data);
   // Do whatever you want to do with the data got from IFrame in Parent form.
}, false); 

Si desea agregar una conexión de seguridad, puede usar esto si la condición en eventer(messageEvent, function (e) {})

if (e.origin == 'http://iframe.example.com') {
    alert(e.data); 
    // Do whatever you want to do with the data got from IFrame in Parent form.
}

Para IE

IFrame interior:

 window.parent.postMessage('{"key":"value"}','*');

Fuera de:

 eventer(messageEvent, function (e) {
   var data = jQuery.parseJSON(e.data);
   doSomething(data.key);
 }, false);

2

Aquí hay una solución simple que utiliza una hoja de estilo generada dinámicamente que se sirve en el mismo servidor que el contenido del iframe. Simplemente, la hoja de estilo "sabe" qué hay en el iframe, y conoce las dimensiones que se usarán para diseñar el iframe. Esto evita las mismas restricciones de la política de origen.

http://www.8degrees.co.nz/2010/06/09/dynamically-resize-an-iframe-depending-on-its-content/

Por lo tanto, el código iframe suministrado tendría una hoja de estilo que lo acompañaría así ...

<link href="http://your.site/path/to/css?contents_id=1234&dom_id=iframe_widget" rel="stylesheet" type="text/css" />
 <iframe id="iframe_widget" src="http://your.site/path/to/content?content_id=1234" frameborder="0" width="100%" scrolling="no"></iframe>

Esto requiere que la lógica del lado del servidor pueda calcular las dimensiones del contenido representado del iframe.


El enlace está caído. ¿Podría compartir el código relevante en su respuesta para que siga siendo relevante para este hilo?
zed

2

Esta respuesta solo es aplicable para sitios web que usan Bootstrap. La función de inserción receptiva de Bootstrap hace el trabajo. Se basa en el ancho (no en la altura) del contenido.

<!-- 16:9 aspect ratio -->
<div class="embed-responsive embed-responsive-16by9">
  <iframe class="embed-responsive-item" src="http://www.youtube.com/embed/WsFWhL4Y84Y"></iframe>
</div>

jsfiddle: http://jsfiddle.net/00qggsjj/2/

http://getbootstrap.com/components/#responsive-embed


Esto funciona para incrustar videos, pero si quiero incrustar una página web con una altura, se desplaza y una altura extraña.
cabaji99

1

Estoy implementando la solución marco en marco de ConroyP para reemplazar una solución basada en la configuración de document.domain, pero descubrí que es bastante difícil determinar la altura del contenido del iframe correctamente en diferentes navegadores (probando con FF11, Ch17 e IE9 en este momento )

ConroyP utiliza:

var height = document.body.scrollHeight;

Pero eso solo funciona en la carga de la página inicial. Mi iframe tiene contenido dinámico y necesito cambiar el tamaño del iframe en ciertos eventos.

Lo que terminé haciendo fue usar diferentes propiedades JS para los diferentes navegadores.

function getDim () {
    var body = document.body,
        html = document.documentElement;

    var bc = body.clientHeight;
    var bo = body.offsetHeight;
    var bs = body.scrollHeight;
    var hc = html.clientHeight;
    var ho = html.offsetHeight;
    var hs = html.scrollHeight;

    var h = Math.max(bc, bo, bs, hc, hs, ho);

    var bd = getBrowserData();

    // Select height property to use depending on browser
    if (bd.isGecko) {
        // FF 11
        h = hc;
    } else if (bd.isChrome) {
        // CH 17
        h = hc;
    } else if (bd.isIE) {
        // IE 9
        h = bs;
    }

    return h;
}

getBrowserData () es la función de detección del navegador "inspirada" en http://docs.sencha.com/core/source/Ext.html#method-Ext-apply de Ext Core

Eso funcionó bien para FF e IE, pero luego hubo problemas con Chrome. Uno de ellos era un problema de tiempo, aparentemente le toma un tiempo a Chrome configurar / detectar la altura del iframe. Y luego Chrome tampoco devolvió la altura del contenido en el iframe correctamente si el iframe era más alto que el contenido. Esto no funcionaría con contenido dinámico cuando se reduce la altura.

Para resolver esto, siempre configuro el iframe a una altura baja antes de detectar la altura del contenido y luego establecer la altura del iframe en su valor correcto.

function resize () {
    // Reset the iframes height to a low value.
    // Otherwise Chrome won't detect the content height of the iframe.
    setIframeHeight(150);

    // Delay getting the dimensions because Chrome needs
    // a few moments to get the correct height.
    setTimeout("getDimAndResize()", 100);
}

El código no está optimizado, es de mi prueba de desarrollo :)

¡Espero que alguien encuentre esto útil!


1
<html>
<head>
<script>
function frameSize(id){
var frameHeight;

document.getElementById(id).height=0 + "px";
if(document.getElementById){
    newheight=document.getElementById(id).contentWindow.document.body.scrollHeight;    
}

document.getElementById(id).height= (frameHeight) + "px";
}
</script>
</head>

<body>

<iframe id="frame"  src="startframe.html" frameborder="0" marginheight="0" hspace=20     width="100%" 

onload="javascript:frameSize('frame');">

<p>This will work, but you need to host it on an http server, you can do it locally.    </p>
</body>
</html>

0

Los gadgets de iGoogle tienen que implementar activamente el cambio de tamaño, por lo que supongo que en un modelo entre dominios no se puede hacer esto sin que el contenido remoto participe de alguna manera. Si su contenido puede enviar un mensaje con el nuevo tamaño a la página del contenedor utilizando técnicas típicas de comunicación entre dominios, entonces el resto es simple.


0

Cuando desee alejar una página web para ajustarla al tamaño del iframe:

  1. Deberías cambiar el tamaño del iframe para que se ajuste al contenido.
  2. Luego, debe alejar todo el iframe con el contenido de la página web cargada

Aquí hay un ejemplo:

<div id="wrap">
   <IFRAME ID="frame" name="Main" src ="http://www.google.com" />
</div>

<style type="text/css">
    #wrap { width: 130px; height: 130px; padding: 0; overflow: hidden; }
    #frame { width: 900px; height: 600px; border: 1px solid black; }
    #frame { zoom:0.15; -moz-transform:scale(0.15);-moz-transform-origin: 0 0; }
</style>

0

Aquí hay un enfoque jQuery que agrega la información en json a través del atributo src del iframe. Aquí hay una demostración, redimensionar y desplazar esta ventana ... la url resultante con json se ve así ... http://fiddle.jshell.net/zippyskippy/RJN3G/show/#{docHeight:5124,windowHeight:1019,scrollHeight: 571} #

Aquí está el código fuente violín http://jsfiddle.net/zippyskippy/RJN3G/

function updateLocation(){

    var loc = window.location.href;
    window.location.href = loc.replace(/#{.*}#/,"") 
        + "#{docHeight:"+$(document).height() 
        + ",windowHeight:"+$(window).height()
        + ",scrollHeight:"+$(window).scrollTop()
        +"}#";

};

//setInterval(updateLocation,500);

$(window).resize(updateLocation);
$(window).scroll(updateLocation);

0

obtener la altura del contenido del iframe y luego dárselo a este iframe

 var iframes = document.getElementsByTagName("iframe");
 for(var i = 0, len = iframes.length; i<len; i++){
      window.frames[i].onload = function(_i){
           return function(){
                     iframes[_i].style.height = window.frames[_i].document.body.scrollHeight + "px";
                     }
      }(i);
 }

1
¿Puede agregar algún tipo de descripción sobre por qué su código responde a la pregunta?
Rhughes

0

Trabajar con jquery en carga (navegador cruzado):

 <iframe src="your_url" marginwidth="0"  marginheight="0" scrolling="No" frameborder="0"  hspace="0" vspace="0" id="containiframe" onload="loaderIframe();" height="100%"  width="100%"></iframe>

function loaderIframe(){
var heightIframe = $('#containiframe').contents().find('body').height();
$('#frame').css("height", heightFrame);
 }  

en cambiar el tamaño en la página receptiva:

$(window).resize(function(){
if($('#containiframe').length !== 0) {
var heightIframe = $('#containiframe').contents().find('body').height();
 $('#frame').css("height", heightFrame);
}
});

-1

Usando jQuery:

parent.html

<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<style>
iframe {
    width: 100%;
    border: 1px solid black;
}
</style>
<script>
function foo(w, h) {
    $("iframe").css({width: w, height: h});
    return true;  // for debug purposes
}
</script>
<iframe src="child.html"></iframe>
</body>

child.html

<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function() {
    var w = $("#container").css("width");
    var h = $("#container").css("height");

    var req = parent.foo(w, h);
    console.log(req); // for debug purposes
});
</script>
<style>
body, html {
    margin: 0;
}
#container {
    width: 500px;
    height: 500px;
    background-color: red;
}
</style>
<div id="container"></div>
</body>

-2

Esto es un poco complicado, ya que debe saber cuándo se cargó la página del iframe, lo cual es difícil cuando no tiene el control de su contenido. Es posible agregar un controlador de carga al iframe, pero lo he intentado en el pasado y tiene un comportamiento muy diferente en todos los navegadores (sin adivinar quién es el más molesto ...). Probablemente tenga que agregar una función a la página de iframe que realiza el cambio de tamaño e inyectar algún script en el contenido que escucha cargar eventos o cambiar el tamaño de eventos, que luego llama a la función anterior. Estoy pensando en agregar una función a la página, ya que desea asegurarse de que sea segura, pero no tengo idea de lo fácil que será hacerlo.


-2

Algo en este sentido, creo, debería funcionar.

parent.document.getElementById(iFrameID).style.height=framedPage.scrollHeight;

Cargue esto con la carga de su cuerpo en el contenido del iframe.


3
Eso no es posible ya que el contenido de los iFrames se carga desde otros dominios, por lo que intentar hacer eso produce un error, como el de Firefox: "Permiso denegado para obtener la propiedad Window.document".
larssg

-4

Tengo una solución fácil y requiere que determine el ancho y la altura en el enlace, intente (funciona con la mayoría de los navegadores):

<a href='#' onClick=" document.getElementById('myform').src='t2.htm';document.getElementById('myform').width='500px'; document.getElementById('myform').height='400px'; return false">500x400</a>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.