¿Existe un cambio de tamaño automático de altura de iframe entre dominios que funcione?

110

Probé algunas soluciones pero no tuve éxito. Me pregunto si existe una solución preferiblemente con un tutorial fácil de seguir.

J82
fuente
2
¿Qué soluciones probaste que no tuvieron éxito?
Yzmir Ramirez
1
Probé este, pero se asusta en los navegadores webkit como se indica en el artículo: css-tricks.com/cross-domain-iframe-resizing Había otro pero no recuerdo la URL.
J82

Respuestas:

68

Tienes tres alternativas:

1. Utilice iFrame-resizer

Esta es una biblioteca simple para mantener los iFrames del tamaño de su contenido. Utiliza las API PostMessage y MutationObserver, con alternativas para IE8-10. También tiene opciones para que la página de contenido solicite que el iFrame que lo contiene tenga un tamaño determinado y también puede cerrar el iFrame cuando haya terminado.

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

2. Utilice Easy XDM (combo PostMessage + Flash)

Easy XDM utiliza una colección de trucos para habilitar la comunicación entre dominios entre diferentes ventanas en varios navegadores, y hay ejemplos para usarlo para cambiar el tamaño de 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/

Easy XDM funciona mediante el uso de PostMessage en los navegadores modernos y una solución basada en Flash como respaldo para los navegadores más antiguos.

Vea también este hilo en Stackoverflow (también hay otros, esta es una pregunta común). Además, Facebook parece utilizar un enfoque similar .

3. Comunicarse a través de un servidor

Otra opción sería enviar la altura del iframe a su servidor y luego sondear desde ese servidor desde la página web principal con JSONP (o usar una encuesta larga si es posible).

Janne Aukia
fuente
1
Para PostMessage, si ya está usando jQuery, es posible que también desee consultar el fantástico complemento postMessage de Ben Alman: benalman.com/projects/jquery-postmessage-plugin
rinogo
8
iFrame-resizer necesita acceso al servidor que aloja el contenido del iframe. Por lo tanto, solo puede usarlo para los dominios que controla.
Hokascha
Buen partido, @Hokascha. El proyecto afirma ser compatible con iframes entre dominios, pero la lectura de los documentos revela que aún requiere acceso del servidor al dominio integrado.
StockB
1
Ninguna de estas "alternativas" son soluciones reales para este problema. El autor de la pregunta quiere establecer la altura de iFrame de los iFrames entre dominios de sitios web sobre los que no tiene control. 1. Requiere acceso al servidor. 2. Requiere software que considero deficiente. Easy XDM se hizo como hace 10 años. La última versión, de 2019, requiere flash . Flash está, afortunadamente, muerto y nadie debería confiar en él. 3. Requiere acceso al servidor.
redanimalwar
26

Obtuve la solución para configurar la altura del iframe de forma dinámica en función de su contenido. Esto funciona para el contenido multidominio. Hay algunos pasos a seguir para lograrlo.

  1. Suponga que ha agregado un iframe en la página web "abc.com/page"

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

  2. A continuación, debe vincular el evento "mensaje" de Windows en la página 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);        
    }
});

En la carga del iframe, debe enviar un mensaje al contenido de la ventana del iframe con el mensaje "FrameHeight":

function setIframeHeight(ifrm) {
   var height = ifrm.contentWindow.postMessage("FrameHeight", "*");   
}
  1. En la página principal que se agregó en iframe aquí "xyz.pqr / contactpage", debe vincular el evento "mensaje" de Windows donde todos los mensajes se recibirán de la ventana principal 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
fuente
3
Funciona sin problemas. ¡Montones de gracias!
Alex Leonov
simplemente este es el método más técnico que pude encontrar, gran trabajo @sudhir, gracias :)
java acm
14

Lo que hice fue comparar el iframe scrollWidth hasta que cambió de tamaño mientras establecía de forma incremental la altura del iframe. Y funcionó bien para mí. Puede ajustar el incremento a lo que desee.

   <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
fuente
4
Probablemente desee agregar un límite al número de bucles, de lo contrario, el 'while' puede degenerar en un bucle infinito.
Kevin Seifert
1
Esto está colapsando mi página.
DDDD
Buena solución simple. Pero en mi caso, el incremento de tamaño está limitado por el tamaño de la pantalla.
Zeta
1

Tengo un script que cae en el iframe con su contenido. También se asegura de que exista iFrameResizer (lo inyecta como un script) y luego realiza el cambio de tamaño.

Incluiré un ejemplo simplificado a continuación.

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

Luego, el contenido del iframe se puede inyectar en cualquier lugar dentro de otra página / sitio usando el script así:

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

El contenido del iframe se inyectará debajo donde coloque la etiqueta de secuencia de comandos.

Espero que esto sea útil para alguien. 👍

ctrlplusb
fuente
Bien, agregué <script ... data-src="http://google.com">y llené el iframe src con él.
BananaAcid
Al momento de escribir, la versión actual es[email protected]
BananaAcid
1
... extendido a un ejemplo completo y utilizable codesandbox.io/s/remote-embed-ifrm-qdb74
BananaAcid
@BananaAcid - su enlace de codesandbox ya no funciona
ctrlplusb
gracias por mencionarlo, los enlaces se han convertido en: codesandbox.io/s/remote-embed-ifrm-yz0xl . (nota: los códigos y la caja "falsifican" al usar varias páginas y pueden bloquearse. La recarga podría ayudar)
BananaAcid
1

Aquí está mi solución simple en esta página. http://lab.ohshiftlabs.com/iframesize/

Así es como funciona;

ingrese la descripción de la imagen aquí

Básicamente, si puede editar la página en otro dominio, puede colocar otra página iframe que pertenezca a su servidor, lo que ahorra altura para las cookies. Con un intervalo de lectura de cookies cuando se actualiza, actualiza la altura del iframe. Eso es todo.

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

Edición: diciembre de 2019

Básicamente, la solución anterior utiliza otro iframe dentro de un iframe. Pero no es una buena solución, por lo que debe seguir esta:

En la página principal:

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

Aquí puedes comprobar m.originde dónde viene.

En la página del marco:

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

Aunque, no olvide que esta no es una forma tan segura. Para hacerlo seguro, actualice * valor (targetOrigin) con el valor deseado. Siga la documentación: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

siniradam
fuente
1
Estos enlaces ahora están muertos (404) y el contenido no se resume en la respuesta. :(
StockB
1
los enlaces todavía están muertos. 404
Arslan Ameer
0

Encontré otra solución del lado del servidor para desarrolladores web que usan PHP para obtener el tamaño de un iframe.

Primero está usando el script PHP del servidor para una llamada externa a través de una función interna: (como file_get_contentscon pero curl y 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>

Necesita mostrar debajo de div ( iframe_reserve) el html generado por la llamada a la función usando un simpleecho curl_get_file_contents("location url iframe","activation proxy")

Después de hacer esto, una función de evento de cuerpo onload con javascript toma la altura del iframe de la página solo con un simple control del contenido div ( iframe_reserve)

Así que solía divHeight = document.getElementById("iframe_reserve").clientHeight;obtener la altura de la página externa a la que vamos a llamar después de enmascarar el contenedor div ( iframe_reserve). Después de esto cargamos el iframe con su buena altura eso es todo.

headmax
fuente
0

Me encontré con este problema mientras trabajaba en algo en el trabajo (usando React). Básicamente, tenemos contenido html externo que guardamos en nuestra tabla de documentos en la base de datos y luego insertamos en la página bajo ciertas circunstancias cuando estás en el conjunto de datos de Documentos.

Entonces, dadas las nlíneas, de las cuales hasta npodrían contener html externo, necesitábamos diseñar un sistema para cambiar automáticamente el tamaño del iframe de cada línea una vez que el contenido esté completamente cargado en cada una. Después de girar un poco mis ruedas, así es como terminé haciéndolo:

  1. Establezca un messagedetector de eventos en el índice de nuestra aplicación React que verifique una clave específica que estableceremos desde el iframe del remitente.
  2. En el componente que realmente muestra los iframes, después de insertar el html externo en él, agrego una <script>etiqueta que esperará a que se activen los iframe window.onload. Una vez que se activa, usamos postMessagepara enviar un mensaje a la ventana principal con información sobre la identificación del iframe, la altura calculada, etc.
  3. Si el origen coincide y la clave se satisface en el oyente de índice, tome el DOM iddel iframe que le pasamos al MessageEventobjeto
  4. Una vez que tengamos el iframe, simplemente establezca la altura del valor que se pasa desde el 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
fuente