¿Cómo hacer que un iframe responda, sin asumir una relación de aspecto? Por ejemplo, el contenido puede tener cualquier ancho o alto, que se desconoce antes de la representación.
Tenga en cuenta que puede usar Javascript.
Ejemplo:
<div id="iframe-container"> <iframe/> </div> Dimensione este iframe-container para que su contenido apenas quepa dentro sin espacio adicional, en otras palabras, hay suficiente espacio para el contenido para que pueda mostrarse sin desplazarse, pero sin exceso de espacio. El contenedor envuelve perfectamente el iframe.
Esto muestra cómo hacer que un iframe responda, asumiendo que la relación de aspecto del contenido es 16:9. Pero en esta pregunta, la relación de aspecto es variable.
Mi solución está en GitHub y JSFiddle .
Presentación de una solución para iframe receptivo sin suposición de relación de aspecto.
El objetivo es cambiar el tamaño del iframe cuando sea necesario, en otras palabras, cuando se cambia el tamaño de la ventana. Esto se realiza con JavaScript para obtener el nuevo tamaño de ventana y cambiar el tamaño del iframe en consecuencia.
NB: no olvide llamar a la función de cambio de tamaño después de cargar la página, ya que la ventana no cambia de tamaño por sí misma después de cargar la página.
El código
índice.html
<!DOCTYPE html> <!-- Onyr for StackOverflow --> <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <title>Responsive Iframe</title> <link rel="stylesheet" type="text/css" href="./style.css"> </head> <body id="page_body"> <h1>Responsive iframe</h1> <div id="video_wrapper"> <iframe id="iframe" src="https://fr.wikipedia.org/wiki/Main_Page"></iframe> </div> <p> Presenting a solution for responsive iframe without aspect ratio assumption.<br><br> </p> <script src="./main.js"></script> </body> </html>estilo.css
html { height: 100%; max-height: 1000px; } body { background-color: #44474a; color: white; margin: 0; padding: 0; } #videoWrapper { position: relative; padding-top: 25px; padding-bottom: 100px; height: 0; margin: 10; } #iframe { top: 0; left: 0; width: 100%; height: 100%; }principal.js
let videoWrapper = document.getElementById("video_wrapper"); let w; let h; let bodyWidth; let bodyHeight; // get window size and resize the iframe function resizeIframeWrapper() { w = window.innerWidth; h = window.innerHeight; videoWrapper.style["width"] = `${w}px`; videoWrapper.style["height"] = `${h - 200}px`; } // call the resize function when windows is resized and after load window.onload = resizeIframeWrapper; window.onresize = resizeIframeWrapper;Pasé un tiempo en esto. Espero que lo disfruten =)
Editar Esta es probablemente la mejor solución genérica. Sin embargo, cuando se hace muy pequeño, el iframe no tiene el tamaño adecuado. Esto es específico para el iframe que está utilizando. No es posible codificar una respuesta adecuada para este fenómeno a menos que tenga el código del iframe, ya que su código no tiene forma de saber qué tamaño prefiere el iframe para que se muestre correctamente.
Solo algunos trucos como el presentado por @Christos Lytras pueden hacer el truco, pero nunca funcionará para todos los iframe. Sólo en una situación particular.
No es posible interactuar con un iFrame de origen diferente usando Javascript para obtener el tamaño; la única forma de hacerlo es usando window.postMessage con targetOrigin establecido en su dominio o el comodín * de la fuente iFrame. Puede representar los contenidos de los diferentes sitios de origen y usar srcdoc , pero eso se considera un truco y no funcionará con SPA y muchas otras páginas más dinámicas.
Supongamos que tenemos dos iFrames del mismo origen, uno de altura corta y ancho fijo:
<!-- iframe-short.html --> <head> <style type="text/css"> html, body { margin: 0 } body { width: 300px; } </style> </head> <body> <div>This is an iFrame</div> <span id="val">(val)</span> </body>y un iFrame de gran altura:
<!-- iframe-long.html --> <head> <style type="text/css"> html, body { margin: 0 } #expander { height: 1200px; } </style> </head> <body> <div>This is a long height iFrame Start</div> <span id="val">(val)</span> <div id="expander"></div> <div>This is a long height iFrame End</div> <span id="val">(val)</span> </body> Podemos obtener el tamaño de iFrame en el evento de load usando iframe.contentWindow.document que enviaremos a la ventana principal usando postMessage :
<div> <iframe id="iframe-local" src="iframe-short.html"></iframe> </div> <div> <iframe id="iframe-long" src="iframe-long.html"></iframe> </div> <script> function iframeLoad() { window.top.postMessage({ iframeWidth: this.contentWindow.document.body.scrollWidth, iframeHeight: this.contentWindow.document.body.scrollHeight, params: { id: this.getAttribute('id') } }); } window.addEventListener('message', ({ data: { iframeWidth, iframeHeight, params: { id } = {} } }) => { // We add 6 pixels because we have "border-width: 3px" for all the iframes if (iframeWidth) { document.getElementById(id).style.width = `${iframeWidth + 6}px`; } if (iframeHeight) { document.getElementById(id).style.height = `${iframeHeight + 6}px`; } }, false); document.getElementById('iframe-local').addEventListener('load', iframeLoad); document.getElementById('iframe-long').addEventListener('load', iframeLoad); </script>Obtendremos el ancho y la altura adecuados para ambos iFrames; puede verificarlo en línea aquí y ver la captura de pantalla aquí .
El método descrito aquí es un truco y debe usarse si es absolutamente necesario y no hay otra forma de evitarlo; no funcionará para la mayoría de las páginas generadas dinámicamente y SPA. El método obtiene el código fuente HTML de la página usando un proxy para eludir la política CORS ( cors-anywhere es una manera fácil de crear un servidor proxy CORS simple y tiene una demostración en línea https://cors-anywhere.herokuapp.com ) y luego inyecta código JS en ese HTML para usar postMessage y enviar el tamaño del iFrame al documento principal. Incluso maneja el evento de cambio de resize de iFrame ( combinado con el width: 100% ) y publica el tamaño de iFrame en el padre.
patchIframeHtml : Una función para parchear el código HTML del iFrame e inyectar Javascript personalizado que usará postMessage para enviar el tamaño del iFrame al padre al load y al cambiar el resize . Si hay un valor para el parámetro de origin , entonces se agregará un elemento HTML <base/> al encabezado usando esa URL de origen, por lo tanto, los URI de HTML como /some/resource/file.ext serán obtenidos correctamente por la URL de origen dentro del iFrame.
function patchIframeHtml(html, origin, params = {}) { // Create a DOM parser const parser = new DOMParser(); // Create a document parsing the HTML as "text/html" const doc = parser.parseFromString(html, 'text/html'); // Create the script element that will be injected to the iFrame const script = doc.createElement('script'); // Set the script code script.textContent = ` window.addEventListener('load', () => { // Set iFrame document "height: auto" and "overlow-y: auto", // so to get auto height. We set "overlow-y: auto" for demontration // and in usage it should be "overlow-y: hidden" document.body.style.height = 'auto'; document.body.style.overflowY = 'auto'; poseResizeMessage(); }); window.addEventListener('resize', poseResizeMessage); function poseResizeMessage() { window.top.postMessage({ // iframeWidth: document.body.scrollWidth, iframeHeight: document.body.scrollHeight, // pass the params as encoded URI JSON string // and decode them back inside iFrame params: JSON.parse(decodeURIComponent('${encodeURIComponent(JSON.stringify(params))}')) }, '*'); } `; // Append the custom script element to the iFrame body doc.body.appendChild(script); // If we have an origin URL, // create a base tag using that origin // and prepend it to the head if (origin) { const base = doc.createElement('base'); base.setAttribute('href', origin); doc.head.prepend(base); } // Return the document altered HTML that contains the injected script return doc.documentElement.outerHTML; }getIframeHtml : Una función para obtener una página HTML sin pasar por el CORS usando un proxy si se establece useProxy param. Puede haber parámetros adicionales que se pasarán a postMessage al enviar datos de tamaño.
function getIframeHtml(url, useProxy = false, params = {}) { return new Promise(resolve => { const xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { // If we use a proxy, // set the origin so it will be placed on a base tag inside iFrame head let origin = useProxy && (new URL(url)).origin; const patchedHtml = patchIframeHtml(xhr.responseText, origin, params); resolve(patchedHtml); } } // Use cors-anywhere proxy if useProxy is set xhr.open('GET', useProxy ? `https://cors-anywhere.herokuapp.com/${url}` : url, true); xhr.send(); }); }La función del controlador de eventos del mensaje es exactamente la misma que en "Tamaño del iFrame del mismo origen" .
Ahora podemos cargar un dominio de origen cruzado dentro de un iFrame con nuestro código JS personalizado inyectado:
<!-- It's important that the iFrame must have a 100% width for the resize event to work --> <iframe id="iframe-cross" style="width: 100%"></iframe> <script> window.addEventListener('DOMContentLoaded', async () => { const crossDomainHtml = await getIframeHtml( 'https://en.wikipedia.org/wiki/HTML', true /* useProxy */, { id: 'iframe-cross' } ); // We use srcdoc attribute to set the iFrame HTML instead of a src URL document.getElementById('iframe-cross').setAttribute('srcdoc', crossDomainHtml); }); </script> Y haremos que el iFrame se ajuste al tamaño de su contenido de altura completa sin ningún desplazamiento vertical, incluso usando overflow-y: auto para el cuerpo del iFrame ( debe estar overflow-y: hidden para que la barra de desplazamiento no parpadee al cambiar el tamaño ).
Puedes consultarlo en línea aquí .
Nuevamente para notar que esto es un truco y debe evitarse ; no podemos acceder al documento iFrame de origen cruzado ni inyectar ningún tipo de cosas.
Hay muchas complicaciones al calcular el tamaño del contenido en un iframe, principalmente debido a que CSS le permite hacer cosas que pueden romper la forma en que mide el tamaño del contenido.
Escribí una biblioteca que se ocupa de todas estas cosas y también funciona entre dominios, puede que le resulte útil.
Haga una cosa, establezca el contenedor Iframe con algo de ancho y altura, establezca la propiedad CSS
.iframe-container{ width:100%; min-height:600px; max-height:100vh; } .iframe-container iframe{ width:100%; height:100%; object-fit:contain; }