Redimensionando um iframe com base no conteúdo

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

Como eu redimensiono os iframes para ajustar a altura do conteúdo dos iframes?

Eu tentei decifrar o javascript que o Google usa, mas ele é ofuscado, e pesquisar na web tem sido infrutífero até agora.

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

Tivemos esse tipo de problema, mas um pouco ao contrário da sua situação. Fornecemos o conteúdo iframed a sites em outros domínios, então a mesma política de origem também foi um problema. Depois de passar muitas horas explorando o google, finalmente encontramos uma (de certa forma) uma solução viável, que você pode adaptar às suas necessidades.

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

Há uma peculiaridade do navegador que 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 que ele tenha iframed, mas nunca em páginas nas quais ele é 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.html pode 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.html pode enviar mensagens para helper.html (iframed), mas não para home.html (a criança não pode se comunicar entre domínios com os pais).

A chave aqui é que o helper.html pode receber mensagens do framed.html e também pode se comunicar com o home.html .

Então, essencialmente, quando framed.html carregado, ele trabalha com a própria altura, informa helper.html , que passa a mensagem para home.html , que pode, então, resize o iframe no qual framed.html fica.

A maneira mais simples que encontramos para passar mensagens de framed.html para helper.html foi através de um argumento de URL. Para fazer isso, o framed.html tem um iframe com src='' especificado. Quando o onload triggersdo, ele avalia sua própria altura e define o src do iframe nesse ponto como helper.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 , o seguinte código javascript é necessário (isto pode ser carregado de um arquivo .js em qualquer domínio, por acaso ..):

   

Em www.bar.net/framed.html :

    

Conteúdo de www.foo.com/helper.html :

       

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

   

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

window.postMessage ()

window.postMessage é um método para ativar com segurança a comunicação entre origens. Normalmente, os 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 ambos http), número de porta (80 sendo o padrão para http) e host (módulo document.domain sendo definido pelas duas páginas para o mesmo valor). O window.postMessage fornece um mecanismo controlado para contornar essa restrição de maneira segura quando usada corretamente.

Resumo

window.postMessage, quando chamado, faz com que um MessageEvent seja despachado na janela de destino quando qualquer script pendente que deve ser executado é concluído (por exemplo, manipuladores de events restantes se window.postMessage for chamado de um manipulador de events, tempos limite pendentes previamente definidos, etc. ). O MessageEvent possui a mensagem type, uma propriedade de dados que é configurada para o valor de string 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 da qual window.postMessage é chamada. (Outras propriedades padrão de events estão presentes com seus valores esperados.)

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

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

jQuery: bondade de script entre domínios

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

Tem demonstração do redimensionamento da janela iframe …

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

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

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

Exemplo barebones …

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

Esboço 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 Cruzadas

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

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

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

  

Eu não sei se você precisa adicionar 30 ao tamanho … 1 funcionou para mim.

FYI : Se você já tem um atributo “height” no seu iFrame, isso apenas adiciona style = “height: xxx”. Isso pode não ser o que você quer.

pode ser um pouco tarde, já que todas as outras respostas são mais antigas 🙂 mas … aqui está a minha solução. Testado em FF real, 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.

Aqui está uma solução simples usando uma folha de estilos gerada dinamicamente, servida pelo mesmo servidor que o conteúdo do iframe. Muito simplesmente, a folha de estilo “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/

Assim, o código de iframe fornecido teria uma folha de estilo como esta …

Isso exige que a lógica do lado do servidor consiga calcular as dimensões do conteúdo processado do iframe.

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

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

SiteB está carregando dentro do siteA site

SiteB site tem essa 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ê quiser adicionar uma conexão de segurança, você pode usar esta condição se no 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); 

Esta resposta é aplicável apenas para 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.

  

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

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

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

ConroyP usa:

 var height = document.body.scrollHeight; 

Mas isso só funciona no carregamento inicial da página. Meu iframe tem conteúdo dynamic e preciso resize o iframe em determinados events.

O que acabei fazendo foi usando diferentes propriedades JS 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 () é uma function de detecção de navegador “inspirada” por http://docs.sencha.com/core/source/Ext.html#method-Ext-apply da Ext Core

Isso funcionou bem para FF e IE, mas depois houve problemas com o Chrome. Um deles foi um problema de tempo, aparentemente demora algum 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 dynamic 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, definir a altura do iframe como seu 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 está otimizado, é do meu teste de desenvolvimento 🙂

Espero que alguém ache isso útil!

        

Os gadgets do iGoogle precisam implementar ativamente o redimensionamento. Por isso, acho que em um modelo de vários domínios não é possível 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.

Quando você quiser reduzir uma página da Web para ajustá-la ao tamanho do iframe:

  1. Você deve resize o iframe para ajustá-lo ao conteúdo
  2. Então você deve diminuir o iframe inteiro com o conteúdo da página da web carregada

Aqui está um exemplo:

 

  

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

Aqui está o violino do 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); 

obter a altura do conteúdo iframe e, em seguida, fornecê-lo a esse iframe

  var iframes = document.getElementsByTagName("iframe"); for(var i = 0, len = iframes.length; i 

Trabalhar com jquery on load (cross browser):

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

em resize na página responsiva:

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

Usando jQuery:

parent.html

       

child.html

     

Isso é um pouco complicado, já que você precisa saber quando a página do iframe foi carregada, o que é difícil quando você não está no controle de seu conteúdo. É possível adicionar um manipulador onload ao iframe, mas eu tentei isso no passado e ele tem um comportamento muito diferente em todos os navegadores (não adivinhe quem é o mais chato …). Você provavelmente teria que adicionar uma function à página do iframe que executa o redimensionamento e injetar algum script no conteúdo que ou deseja carregar events ou resize events, o que chama a function anterior. Estou pensando em adicionar uma function à página, já que você quer ter certeza de que ela é segura, mas não tenho ideia de como será fácil fazer isso.

Algo nas linhas deste que eu acredito deve funcionar.

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

Carregue isso com o seu corpo onload no conteúdo do iframe.

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

 500x400