Existe um redimensionador automático de altura de iframe entre domínios que funciona?

110

Tentei algumas soluções, mas não tive sucesso. Estou me perguntando se existe uma solução por aí, de preferência com um tutorial fácil de seguir.

J82
fonte
2
Que soluções você tentou e não teve sucesso?
Yzmir Ramirez,
1
Eu tentei este, mas ele enlouquece nos navegadores do webkit, conforme declarado no artigo: css-tricks.com/cross-domain-iframe-resizing Havia outro, mas não consigo lembrar o url.
J82,

Respostas:

68

Você tem três alternativas:

1. Use o redimensionador de iFrame

Esta é uma biblioteca simples para manter os iFrames dimensionados de acordo com seu conteúdo. Ele usa as APIs PostMessage e MutationObserver, com fallback para o IE8-10. Ele também tem opções para a página de conteúdo solicitar que o iframe contido tenha um determinado tamanho e também pode fechar o iframe quando terminar de usá-lo.

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

2. Use Easy XDM (combinação PostMessage + Flash)

O Easy XDM usa uma coleção de truques para permitir a comunicação entre domínios diferentes em vários navegadores e há exemplos de como usá-lo para redimensionar o iframe:

http://easyxdm.net/wp/2010/03/17/resize-iframe-based-on-content/

http://kinsey.no/blog/index.php/2010/02/19/resizing-iframes-using-easyxdm/

O Easy XDM funciona usando PostMessage em navegadores modernos e uma solução baseada em Flash como alternativa para navegadores mais antigos.

Veja também este tópico sobre Stackoverflow (também há outros, esta é uma pergunta comum). Além disso, o Facebook parece usar uma abordagem semelhante .

3. Comunique-se por meio de um servidor

Outra opção seria enviar a altura do iframe para o seu servidor e, em seguida, pesquisar desse servidor a partir da página da web pai com JSONP (ou usar uma longa pesquisa, se possível).

Janne Aukia
fonte
1
Para PostMessage, se você já estiver usando jQuery, você também pode querer dar uma olhada no fantástico plugin postMessage de Ben Alman: benalman.com/projects/jquery-postmessage-plugin
rinogo
8
O redimensionador de iFrame precisa acessar o servidor que hospeda o conteúdo do iframe. Portanto, você só pode usá-lo para domínios que você controla.
Hokascha
Boa pegada, @Hokascha. O projeto alega suporte para iframes entre domínios, mas a leitura dos documentos revela que ele ainda requer acesso de servidor ao domínio incorporado.
StockB
1
Nenhuma dessas "alternativas" são soluções reais para esse problema. O solicitante deseja definir a altura do iframe de iframes de domínio cruzado de sites sobre os quais eles não têm controle. 1. Requer acesso ao servidor. 2. Requer software que considero pobre. O Easy XDM foi feito há 10 anos. A versão mais recente, de 2019, requer flash . Felizmente, o Flash está morto e ninguém deve confiar nele. 3. Requer acesso ao servidor.
redanimalwar
26

Eu tenho a solução para definir a altura do iframe dinamicamente com base em seu conteúdo. Isso funciona para o conteúdo de domínio cruzado. Existem alguns passos a seguir para o conseguir.

  1. Suponha que você tenha adicionado iframe na página da web "abc.com/page"

    <div> <iframe id="IframeId" src="http://xyz.pqr/contactpage" style="width:100%;" onload="setIframeHeight(this)"></iframe> </div>

  2. Em seguida, você deve vincular o evento de "mensagem" do Windows à página da web "abc.com/page"

window.addEventListener('message', function (event) {
//Here We have to check content of the message event  for safety purpose
//event data contains message sent from page added in iframe as shown in step 3
if (event.data.hasOwnProperty("FrameHeight")) {
        //Set height of the Iframe
        $("#IframeId").css("height", event.data.FrameHeight);        
    }
});

No carregamento do iframe, você deve enviar uma mensagem para o conteúdo da janela do iframe com a mensagem "FrameHeight":

function setIframeHeight(ifrm) {
   var height = ifrm.contentWindow.postMessage("FrameHeight", "*");   
}
  1. Na página principal adicionada sob iframe aqui "xyz.pqr / contactpage", você deve vincular o evento "mensagem" do windows onde todas as mensagens vão receber da janela pai de "abc.com/page"
window.addEventListener('message', function (event) {

    // Need to check for safety as we are going to process only our messages
    // So Check whether event with data(which contains any object) contains our message here its "FrameHeight"
   if (event.data == "FrameHeight") {

        //event.source contains parent page window object 
        //which we are going to use to send message back to main page here "abc.com/page"

        //parentSourceWindow = event.source;

        //Calculate the maximum height of the page
        var body = document.body, html = document.documentElement;
        var height = Math.max(body.scrollHeight, body.offsetHeight,
            html.clientHeight, html.scrollHeight, html.offsetHeight);

       // Send height back to parent page "abc.com/page"
        event.source.postMessage({ "FrameHeight": height }, "*");       
    }
});
Sudhir
fonte
3
Funciona suavemente. Muito obrigado!
Alex Leonov
simplesmente este é o método mais técnico que consegui encontrar, ótimo trabalho @sudhir, obrigado :)
java acm
14

O que fiz foi comparar o iframe scrollWidth até que ele mudasse de tamanho enquanto eu configurava incrementalmente a altura do IFrame. E funcionou bem para mim. Você pode ajustar o incremento para o que desejar.

   <script type="text/javascript">
    function AdjustIFrame(id) {
        var frame = document.getElementById(id);
        var maxW = frame.scrollWidth;
        var minW = maxW;
        var FrameH = 100; //IFrame starting height
        frame.style.height = FrameH + "px"

        while (minW == maxW) {
            FrameH = FrameH + 100; //Increment
            frame.style.height = FrameH + "px";
            minW = frame.scrollWidth;
        }
    }

   </script>


<iframe id="RefFrame" onload="AdjustIFrame('RefFrame');" class="RefFrame"
    src="http://www.YourUrl.com"></iframe>
Troy Mitchel
fonte
4
você provavelmente deseja adicionar um limite ao número de loops, caso contrário, o 'enquanto' pode degenerar em um loop infinito.
Kevin Seifert
1
Isso está travando minha página.
DDDD de
Boa solução simples. Mas no meu caso, o incremento de tamanho é limitado pelo tamanho da tela.
Zeta
1

Eu tenho um script que cai no iframe com seu conteúdo. Ele também garante que iFrameResizer existe (ele o injeta como um script) e, em seguida, faz o redimensionamento.

Vou apresentar um exemplo simplificado abaixo.

// /js/embed-iframe-content.js

(function(){
    // Note the id, we need to set this correctly on the script tag responsible for
    // requesting this file.
    var me = document.getElementById('my-iframe-content-loader-script-tag');

    function loadIFrame() {
        var ifrm = document.createElement('iframe');
        ifrm.id = 'my-iframe-identifier';
        ifrm.setAttribute('src', 'http://www.google.com');
        ifrm.style.width = '100%';
        ifrm.style.border = 0;
        // we initially hide the iframe to avoid seeing the iframe resizing
        ifrm.style.opacity = 0;
        ifrm.onload = function () {
            // this will resize our iframe
            iFrameResize({ log: true }, '#my-iframe-identifier');
            // make our iframe visible
            ifrm.style.opacity = 1;
        };

        me.insertAdjacentElement('afterend', ifrm);
    }

    if (!window.iFrameResize) {
        // We first need to ensure we inject the js required to resize our iframe.

        var resizerScriptTag = document.createElement('script');
        resizerScriptTag.type = 'text/javascript';

        // IMPORTANT: insert the script tag before attaching the onload and setting the src.
        me.insertAdjacentElement('afterend', ifrm);

        // IMPORTANT: attach the onload before setting the src.
        resizerScriptTag.onload = loadIFrame;

        // This a CDN resource to get the iFrameResizer code.
        // NOTE: You must have the below "coupled" script hosted by the content that
        // is loaded within the iframe:
        // https://unpkg.com/[email protected]/js/iframeResizer.contentWindow.min.js
        resizerScriptTag.src = 'https://unpkg.com/[email protected]/js/iframeResizer.min.js';
    } else {
        // Cool, the iFrameResizer exists so we can just load our iframe.
        loadIFrame();
    }    
}())

Então, o conteúdo do iframe pode ser injetado em qualquer lugar dentro de outra página / site usando o script como:

<script
  id="my-iframe-content-loader-script-tag"
  type="text/javascript"
  src="/js/embed-iframe-content.js"
></script>

O conteúdo do iframe será injetado abaixo de onde quer que você coloque a tag do script.

Espero que isto seja útil para alguém. 👍

ctrlplusb
fonte
Legal, adicionei <script ... data-src="http://google.com">e preenchi o iframe src com ele.
BananaAcid
No momento da redação, a versão atual é[email protected]
BananaAcid
1
... estendido para um exemplo totalmente utilizável codesandbox.io/s/remote-embed-ifrm-qdb74
BananaAcid
@BananaAcid - seu link codesandbox não funciona mais
ctrlplusb
obrigado por mencioná-lo, os links se tornaram: codesandbox.io/s/remote-embed-ifrm-yz0xl . (observação: codesandbox "falsificam" usando várias páginas e podem travar. Recarregar pode ajudar)
BananaAcid
1

Aqui está minha solução simples nesta página. http://lab.ohshiftlabs.com/iframesize/

É assim que funciona;

insira a descrição da imagem aqui

Basicamente, se você pode editar a página em outro domínio, você pode colocar outra página iframe que pertence ao seu servidor que economiza altura para cookies. Com um intervalo de leitura de cookies quando ele é atualizado, atualize a altura do iframe. Isso é tudo.

Baixar; http://lab.ohshiftlabs.com/iframesize/iframesizepost.zip

Editar: dezembro de 2019

A solução acima usa basicamente outro iframe dentro de um iframe. O terceiro iframe pertence ao domínio da página superior, que você chama esta página com uma string de consulta que salva o valor do tamanho em um cookie, a página externa verifica essa consulta com algum intervalo. Mas não é uma boa solução, então você deve seguir esta:

Na página inicial:

window.addEventListener("message", (m)=>{iframeResizingFunction(m)});

Aqui você pode verificar m.originde onde vem.

Página na moldura:

window.parent.postMessage({ width: 640, height:480 }, "*")

Embora, por favor, não se esqueça de que essa não é uma maneira tão segura. Para torná-lo seguro, atualize o valor * (targetOrigin) com o valor desejado. Siga a documentação: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

siniradam
fonte
1
Esses links estão mortos (404) e o conteúdo não está resumido na resposta. :(
StockB
1
links ainda estão mortos. 404
Arslan Ameer
0

Eu encontrei outra solução do lado do servidor para desenvolvimento web usando PHP para obter o tamanho de um iframe.

Primeiro, use o script de servidor PHP para uma chamada externa via função interna: (como um file_get_contentswith, mas curl e dom).

function curl_get_file_contents($url,$proxyActivation=false) {
    global $proxy;
    $c = curl_init();
    curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($c, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7");
    curl_setopt($c, CURLOPT_REFERER, $url);
    curl_setopt($c, CURLOPT_URL, $url);
    curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
    if($proxyActivation) {
        curl_setopt($c, CURLOPT_PROXY, $proxy);
    }
    $contents = curl_exec($c);
    curl_close($c);
    $dom = new DOMDocument();
    $dom->preserveWhiteSpace = false;
    @$dom->loadHTML($contents);
    $form = $dom->getElementsByTagName("body")->item(0);
    if ($contents) //si on a du contenu
        return $dom->saveHTML();
    else
        return FALSE;
}
$url = "http://www.google.com"; //Exernal url test to iframe
<html>
    <head>
    <script type="text/javascript">

    </script>
    <style type="text/css">
    #iframe_reserve {
        width: 560px;
        height: 228px
    }
    </style>
    </head>

    <body>
        <div id="iframe_reserve"><?php echo curl_get_file_contents($url); ?></div>

        <iframe id="myiframe" src="http://www.google.com" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"  style="overflow:none; width:100%; display:none"></iframe>

        <script type="text/javascript">
            window.onload = function(){
            document.getElementById("iframe_reserve").style.display = "block";
            var divHeight = document.getElementById("iframe_reserve").clientHeight;
            document.getElementById("iframe_reserve").style.display = "none";
            document.getElementById("myiframe").style.display = "block";
            document.getElementById("myiframe").style.height = divHeight;
            alert(divHeight);
            };
        </script>
    </body>
</html>

Você precisa exibir sob div ( iframe_reserve) o html gerado pela chamada de função usando um simplesecho curl_get_file_contents("location url iframe","activation proxy")

Depois de fazer isso, uma função de evento de corpo onload com javascript toma a altura do iframe da página apenas com um simples controle do conteúdo div ( iframe_reserve)

Então, eu costumava divHeight = document.getElementById("iframe_reserve").clientHeight;obter a altura da página externa que vamos chamar depois de mascarar o contêiner div ( iframe_reserve). Depois disso, carregamos o iframe com sua altura boa, só isso.

headmax
fonte
0

Encontrei esse problema enquanto trabalhava em algo no trabalho (usando o React). Basicamente, temos algum conteúdo html externo que salvamos em nossa tabela de documentos no banco de dados e, em seguida, inserimos na página em certas circunstâncias quando você está no conjunto de dados Documentos.

Portanto, dados ninlines, dos quais até npoderiam conter html externo, precisávamos criar um sistema para redimensionar automaticamente o iframe de cada inline assim que o conteúdo fosse totalmente carregado em cada um. Depois de girar minhas rodas um pouco, foi assim que acabei fazendo isso:

  1. Defina um messageouvinte de evento no índice de nosso aplicativo React que verifica se há uma chave específica que definiremos no iframe do remetente.
  2. No componente que realmente renderiza os iframes, após inserir o html externo nele, acrescento uma <script>tag que aguardará o window.onloaddisparo dos iframes . Assim que disparar, usamos postMessagepara enviar uma mensagem para a janela pai com informações sobre o id do iframe, altura computada, etc.
  3. Se a origem corresponder e a chave for satisfeita no listener do índice, pegue o DOM iddo iframe que passamos no MessageEventobjeto
  4. Assim que tivermos o iframe, basta definir a altura a partir do valor que é passado do iframe postMessage.
// index
if (window.postMessage) {
    window.addEventListener("message", (messageEvent) => {
        if (
            messageEvent.data.origin &&
            messageEvent.data.origin === "company-name-iframe"
        ) {
            const iframe = document.getElementById(messageEvent.data.id)
            // this is the only way to ensure that the height of the iframe container matches its body height
            iframe.style.height = `${messageEvent.data.height}px`
            // by default, the iframe will not expand to fill the width of its parent
            iframe.style.width = "100%"
            // the iframe should take precedence over all pointer events of its immediate parent
            // (you can still click around the iframe to segue, for example, but all content of the iframe
            // will act like it has been directly inserted into the DOM)
            iframe.style.pointerEvents = "all"
            // by default, iframes have an ugly web-1.0 border
            iframe.style.border = "none"
        }
    })
}
// in component that renders n iframes
<iframe
    id={`${props.id}-iframe`}
    src={(() => {
        const html = [`data:text/html,${encodeURIComponent(props.thirdLineData)}`]
        if (window.parent.postMessage) {
            html.push(
                `
                <script>
                window.onload = function(event) {
                    window.parent.postMessage(
                        {
                            height: document.body.scrollHeight,
                            id: "${props.id}-iframe",
                            origin: "company-name-iframe",
                        },
                        "${window.location.origin}"
                    );
                };
                </script>
                `
            )
        }

        return html.join("\n")
    })()}
    onLoad={(event) => {
        // if the browser does not enforce a cross-origin policy,
        // then just access the height directly instead
        try {
            const { target } = event
            const contentDocument = (
                target.contentDocument ||
                // Earlier versions of IE or IE8+ where !DOCTYPE is not specified
                target.contentWindow.document
            )
            if (contentDocument) {
                target.style.height = `${contentDocument.body.scrollHeight}px`
            }
        } catch (error) {
            const expectedError = (
                `Blocked a frame with origin "${window.location.origin}" ` +
                `from accessing a cross-origin frame.`
            )
            if (error.message !== expectedError) {
                /* eslint-disable no-console */
                console.err(
                    `An error (${error.message}) ocurred while trying to check to see ` +
                    "if the inner iframe is accessible or not depending " +
                    "on the browser cross-origin policy"
                )
            }
        }
    }}
/>
John Duncan
fonte