Redimensionando um iframe com base no conteúdo

498

Estou trabalhando em um aplicativo semelhante ao iGoogle. O conteúdo de outros aplicativos (em outros domínios) é mostrado usando iframes.

Como redimensiono os iframes para caber na altura do conteúdo dos iframes?

Tentei decifrar o javascript que o Google usa, mas ele é ofuscado, e a pesquisa na web tem sido infrutífera até agora.

Atualização: observe que o conteúdo é carregado de outros domínios; portanto, a política de mesma origem se aplica.

Larssg
fonte

Respostas:

584

Tivemos esse tipo de problema, mas ligeiramente ao contrário da sua situação - estávamos fornecendo o conteúdo com iframes para sites em outros domínios, portanto, a mesma política de origem também era um problema. Depois de muitas horas vasculhando o Google, finalmente encontramos uma solução (um pouco ..) viável, que você pode adaptar às suas necessidades.

Existe uma maneira de contornar a mesma política de origem, mas exige alterações no conteúdo iframed e na página de enquadramento. Portanto, se você não puder solicitar alterações nos dois lados, esse método não será muito útil para você, Estou com medo.

Existe uma peculiaridade do navegador que nos permite contornar a mesma política de origem - o javascript pode se comunicar com páginas em seu próprio domínio ou com páginas com iframed, mas nunca com páginas em que está enquadrado, por exemplo, se você tiver:

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

então home.htmlpode se comunicar com framed.html(iframed) e helper.html(mesmo domínio).

 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.htmlpode enviar mensagens para helper.html(iframed), mas não home.html (o filho não pode se comunicar entre domínios com o pai).

A chave aqui é que helper.htmlpode receber mensagens de framed.htmle também pode se comunicar com home.html.

Então, essencialmente, quando framed.htmlcarrega, ele calcula sua própria altura, diz helper.html, que passa a mensagem para home.html, o que pode redimensionar o iframe no qual framed.htmlestá sentado.

A maneira mais simples pela qual descobrimos transmitir mensagens de framed.htmlpara helper.htmlfoi através de um argumento de URL. Para fazer isso, framed.htmlpossui um iframe src=''especificado. Quando é onloadacionado, ele avalia sua própria altura e define o src do iframe neste momento comohelper.html?height=N

Há uma explicação aqui de como o Facebook lida com isso, o que pode ser um pouco mais claro do que o meu acima!


Código

Em www.foo.com/home.html, é necessário o seguinte código javascript (isso pode ser carregado a partir de um arquivo .js em qualquer domínio, aliás ..):

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

Em 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>

Conteúdo 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>
ConroyP
fonte
7
verdadeiramente cheio de insight. Fiquei me perguntando por que o igoogle.com tinha um arquivo helper.html! Obrigado
IEnumerator
9
Incrível, obrigado! Fiz algumas adições: Use o jQuery para obter a altura do corpo em framed.html (problema de FF, o corpo continuou crescendo): var height = $ (document.body) .height (); Usou um evento onload do corpo para criar o quadro em home.html semelhante à sua abordagem em framed.html. Endereços que não atualizam o quadro na atualização no FF e Safari.
Abdullah Jibaly 31/08/09
5
Gênio! Além das adições de Abdullah: em vez de definir a parentIframeResize()carga do corpo a ser chamada , usei o JQuery para capturar o carregamento da página e qualquer evento de redimensionamento: $(document).ready(iframeResizePipe); $(window).resize(iframeResizePipe);Isso permite definir o iframe width="100%"e se a largura da janela mudar para tornar o texto quebrado ou algo assim, O quadro interno reconhecerá que precisa ser redimensionado.
StriplingWarrior
9
Sugiro dar ao iframe id = frame_name_here um atributo de altura padrão, como height = "2000". Isso significa que quando a página carrega, o conteúdo emoldurado fica visível. Quando o redimensionamento acontece, não há "tremulação". O redimensionamento está encolhendo / expandindo abaixo da dobra da página. Se você conhece a altura máxima do seu conteúdo iframed, use esse valor ... isso também resulta em uma melhor experiência noscript.
Chris Jacob
3
Sete anos depois, ainda é a melhor solução entre navegadores e domínios que encontrei. Bravo.
FurryWombat
81

Se você não precisar manipular o conteúdo iframe de um domínio diferente, tente este código, ele resolverá o problema completamente e é simples:

<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>
Ahmy
fonte
9
Pode ser mais ideal passar o próprio elemento para a função para evitar todos aqueles que document.getElementById(id)você possui e simplificar o código.
Muhd
1
Isso não funciona para mim, pois o contentWindow.document.body.scrollHeight contém 0 e não a altura real.
kroiz
4
Esta solução, como todas as outras que tentei, funciona apenas se a altura aumentar, se de uma página alta você navegar para uma mais curta, a altura ainda será definida de acordo com a altura da primeira. Alguém encontrou uma solução para este problema?
Eugenio
6
@Eugenio e todos os outros, inclusive eu, para corrigir esse problema final: consulte stackoverflow.com/questions/3053072/… Antes de solicitar a altura do documento dentro do iframe, defina a altura do objeto iframe como "auto". Note que você deve usar style.height não apenas altura como na resposta acima
Kyle
7
Não entendi ... você verifica a existência de document.getElementById e, em seguida, chama fora da área marcada?
Legolas
41

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

window.postMessage ()

window.postMessage é um método para ativar com segurança a comunicação de origem cruzada. Normalmente, scripts em páginas diferentes só podem acessar um ao outro se, e somente se, as páginas que os executaram estiverem em locais com o mesmo protocolo (geralmente http), número da porta (80 sendo o padrão para http) e host (módulo document.domain sendo definido por ambas as páginas com o mesmo valor). O window.postMessage fornece um mecanismo controlado para contornar essa restrição de forma segura quando usada corretamente.

Sumário

window.postMessage, quando chamado, faz com que um MessageEvent seja despachado na janela de destino quando qualquer script pendente que deve ser executado for concluído (por exemplo, manipuladores de eventos restantes, se window.postMessage for chamado de um manipulador de eventos, tempos limite pendentes definidos anteriormente, etc. ) O MessageEvent possui o tipo message, uma propriedade de dados que é definida como o valor da sequência do primeiro argumento fornecido para window.postMessage, uma propriedade de origem correspondente à origem do documento principal na janela que chama window.postMessage na janela de tempo. postMessage foi chamado e uma propriedade de origem que é a janela a partir da qual window.postMessage é chamado. (Outras propriedades padrão de eventos estão presentes com seus valores esperados.)

A biblioteca do iFrame-Resizer usa o postMessage para manter um tamanho do iFrame em seu conteúdo, juntamente com o MutationObserver para detectar alterações no conteúdo e não depende do jQuery.

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

jQuery: qualidade de script entre domínios

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

Tem demonstração de redimensionamento da janela de iframe ...

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

Este artigo mostra como remover a dependência do jQuery ... Plus possui muitas informações e links úteis para outras soluções.

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

Exemplo de Barebones ...

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

Rascunho de trabalho do HTML 5 em window.postMessage

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

John Resig em Mensagens entre janelas

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

Chris Jacob
fonte
O postMessage por si só não é entre navegadores, mas o plug-in jQuery parece fazer um bom trabalho fingindo quando necessário. O único problema real é o "lixo" adicionado à barra de URL nos casos em que o postMessage não é suportado.
Herms
2
postMessage parece ser o X-Browser. Somente o IE precisa de bugs ou coisas para cuidar: 1. Comunicação apenas entre quadros ou iframes 2. Somente capaz de postar strings. Veja: caniuse.com/x-doc-messaging
Christian Kuetbach
3
Teria sido bom ver você responder a pergunta em vez de explicar sobre uma tecnologia assistiva que ajuda com uma solução ..
hitautodestruct
O periscopedata está usando o postMessage, se for bom o suficiente para eles, é bom o suficiente para nós. Aqui estão os documentos deles: doc.periscopedata.com/docv2/embed-api-options
Yevgeniy Afanasyev
8

A maneira mais simples de usar o jQuery:

$("iframe")
.attr({"scrolling": "no", "src":"http://www.someotherlink.com/"})
.load(function() {
    $(this).css("height", $(this).contents().height() + "px");
});
Omer Arshad
fonte
14
Não entre domínios, conforme solicitado.
Willem
6

A solução em http://www.phinesolutions.com/use-jquery-to-adjust-the-iframe-height.html funciona muito bem (usa jQuery):

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

Não sei se você precisa adicionar 30 ao comprimento ... trabalhei para mim.

FYI : Se você já possui um atributo "height" no seu iFrame, isso apenas adiciona style = "height: xxx". Pode não ser o que você deseja.

Macho Matt
fonte
7
Não entre domínios, não.
Volomike
Exatamente o que eu precisava, pois não estou trabalhando em vários domínios, +1 para html válido.
WildJoe
4

pode ser um pouco tarde, pois todas as outras respostas são mais antigas :-) mas ... aqui está a minha solução. Testado no real FF, Chrome e 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 isso ajude alguém.

ddlab
fonte
@pashute - você quer dizer "domínio cruzado"?
21418 Luke
desculpa. Significado: Não funciona em várias plataformas ... Aqui está uma lista pública: gist.github.com/pashute/c4705ce7f767f50fdf56d0030ecf9192 Obtém um Recusado para executar um erro. Alterar o script para type = "text / javascript" não ajuda. Nem definir a largura e a altura do iframe (digamos 80%).
pashute
4

Finalmente, encontrei outra solução para enviar dados para o site pai do iframe usando window.postMessage(message, targetOrigin); . Aqui eu explico como eu fiz.

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

O siteB está carregando dentro do site

SiteB site tem esta linha

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

ou

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

Então siteA tem este código a seguir

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

Se você deseja adicionar uma conexão de segurança, use esta condição se 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 o IE

Dentro do IFrame:

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

Lado de fora:

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

Aqui está uma solução simples, usando uma folha de estilos gerada dinamicamente e atendida pelo mesmo servidor que o conteúdo do iframe. Simplesmente a folha de estilos "sabe" o que está no iframe e conhece as dimensões a serem usadas para estilizar o iframe. Isso contorna as mesmas restrições de política de origem.

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

Portanto, o código iframe fornecido teria uma folha de estilos que o acompanha ...

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

Isso requer que a lógica do servidor seja capaz de calcular as dimensões do conteúdo renderizado do iframe.

Vaughan Rowsell
fonte
O link está inoperante. Você poderia compartilhar o código relevante em sua resposta para que ele permaneça relevante para este tópico?
zed
2

Esta resposta é aplicável apenas a sites que usam o Bootstrap. O recurso de incorporação responsivo do Bootstrap faz o trabalho. É baseado na largura (não na altura) do conteúdo.

<!-- 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

Razan Paul
fonte
Isso funciona para incorporar vídeos, mas se eu quiser incorporar uma página da Web com uma altura, ela fica rolando e uma altura estranha.
Cabaji99 29/09/16
1

Estou implementando a solução frame-in-frame da ConroyP para substituir uma solução com base na configuração document.domain, mas achei bastante difícil determinar a altura do conteúdo do iframe corretamente em diferentes navegadores (testando com FF11, Ch17 e IE9 agora )

O ConroyP usa:

var height = document.body.scrollHeight;

Mas isso só funciona no carregamento da página inicial. Meu iframe tem conteúdo dinâmico e preciso redimensionar o iframe em determinados eventos.

O que acabei fazendo foi usar propriedades JS diferentes para os 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 () é a função de detecção de navegador "inspirada" pelo http://docs.sencha.com/core/source/Ext.html#method-Ext-apply do Ext Core

Isso funcionou bem para o FF e o IE, mas houve problemas com o Chrome. Uma delas foi um problema de tempo, aparentemente leva um tempo para o Chrome definir / detectar a altura do iframe. E o Chrome também nunca retornou a altura do conteúdo no iframe corretamente se o iframe fosse maior que o conteúdo. Isso não funcionaria com conteúdo dinâmico quando a altura é reduzida.

Para resolver isso, sempre defino o iframe para uma altura baixa antes de detectar a altura do conteúdo e, em seguida, defina a altura do iframe para o valor correto.

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

O código não é otimizado, é do meu teste de desenvolvimento :)

Espero que alguém ache isso útil!

Tor Händevik
fonte
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>
webd3sign
fonte
0

Os gadgets do iGoogle precisam implementar ativamente o redimensionamento. Portanto, em um modelo entre domínios, você não pode fazer isso sem que o conteúdo remoto participe de alguma forma. Se o seu conteúdo puder enviar uma mensagem com o novo tamanho para a página do contêiner usando técnicas típicas de comunicação entre domínios, o resto será simples.

Joeri Sebrechts
fonte
0

Quando você deseja diminuir o zoom de uma página da Web para ajustá-la ao tamanho do iframe:

  1. Você deve redimensionar o iframe para ajustá-lo ao conteúdo
  2. Em seguida, você deve diminuir o zoom de todo o iframe com o conteúdo da página da Web carregado

Aqui está um exemplo:

<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>
kaw
fonte
0

Aqui está uma abordagem jQuery que adiciona as informações em json através do atributo src do iframe. Aqui está uma demonstração, redimensione e role esta janela .. o URL resultante com json se parece com isso ... http://fiddle.jshell.net/zippyskippy/RJN3G/show/#{docHeight:5124,windowHeight:1019,scrollHeight: 571} #

Aqui está o código fonte 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);
chad steele
fonte
0

obter a altura do conteúdo do iframe e atribuí-lo a esse 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);
 }
YellowGlue
fonte
1
Você pode adicionar algum tipo de descrição sobre por que seu código responde à pergunta?
rhughes
0

Trabalhe com jquery on load (cross browser):

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

ao redimensionar na página responsiva:

$(window).resize(function(){
if($('#containiframe').length !== 0) {
var heightIframe = $('#containiframe').contents().find('body').height();
 $('#frame').css("height", heightFrame);
}
});
Mario Gonzales Flores
fonte
-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>
Nino Škopac
fonte
-2

Isso é um pouco complicado, pois você precisa saber quando a página iframe foi carregada, o que é particularmente difícil quando você não controla o conteúdo. É possível adicionar um manipulador de onload ao iframe, mas eu tentei isso no passado e ele tem um comportamento muito diferente entre os navegadores (não adivinhe quem é o mais irritante ...). Você provavelmente teria que adicionar uma função à página iframe que executa o redimensionamento e injetar algum script no conteúdo que ouve para carregar eventos ou redimensionar eventos, que então chama a função anterior. Estou pensando em adicionar uma função à página, pois você deseja garantir sua segurança, mas não tenho ideia de como será fácil fazê-lo.

roryf
fonte
-2

Algo nas linhas deste que eu acredito deve funcionar.

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

Carregue isso com seu corpo carregado no conteúdo do iframe.

Ólafur Waage
fonte
3
Isso não é possível, pois o conteúdo dos iFrames é carregado de outros domínios; portanto, tentar fazer isso resulta em um erro, como este no Firefox: "Permissão negada para obter a propriedade Window.document".
larssg 30/09/08
-4

Eu tenho uma solução fácil e requer que você determine a largura e a altura no link, tente (funciona com a maioria dos navegadores):

<a href='#' onClick=" document.getElementById('myform').src='t2.htm';document.getElementById('myform').width='500px'; document.getElementById('myform').height='400px'; return false">500x400</a>
فيصل خلبوص
fonte