如何在没有宽高比假设的情况下使iframe响应? [英] How to make an iframe responsive without aspect ratio assumption?

查看:86
本文介绍了如何在没有宽高比假设的情况下使iframe响应?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在不假定宽高比的情况下使iframe响应?例如,内容可以具有任何宽度或高度,在渲染之前是未知的.

How to make an iframe responsive, without assuming an aspect ratio? For example, the content may have any width or height, which is unknown before rendering.

注意,您可以使用Javascript.

Note, you can use Javascript.

示例:

<div id="iframe-container">
    <iframe/>
</div>

调整此iframe-container的大小,以使其内容几乎不能容纳在内部而没有多余的空间,换句话说,有足够的空间容纳内容,因此无需滚动即可显示内容,但没有多余的空间.容器完美地包裹了iframe.

Size this iframe-container so that contents of it barely fit inside without extra space, in other words, there is enough space for content so it can be shown without scrolling, but no excess space. Container wraps the iframe perfectly.

展示了如何使iframe响应,假设内容的长宽比为16:9.但是在这个问题上,长宽比是可变的.

This shows how to make an iframe responsive, assuming the aspect ratio of the content is 16:9. But in this question, the aspect ratio is variable.

推荐答案

无法使用Javascript与其他原始iFrame进行交互,以获取其大小;唯一的方法是使用 window.postMessage ,其中targetOrigin设置为您的域,或者来自iFrame源的Wildchar *.您可以代理不同来源站点的内容并使用srcdoc,但这被认为是 hack ,它不适用于SPA和许多其他动态页面.

It is not possible to interact with a different origin iFrame using Javascript so to get the size of it; the only way to do it is by using window.postMessage with the targetOrigin set to your domain or the wildchar * from iFrame source. You can proxy the contents of the different origin sites and use srcdoc, but that is considered a hack and it won't work with SPAs and many other more dynamic pages.

假设我们有两个相同的原始iFrame,其中一个是矮高和固定宽度:

Suppose we have two same origin iFrames, one of short height and fixed width:

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

和长高的iFrame:

and a long height iFrame:

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

我们可以使用iframe.contentWindow.documentload事件上获取iFrame大小,然后使用

We can get iFrame size on load event using iframe.contentWindow.document that we'll send to the parent window using 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>

我们将为两个iFrame获得适当的宽度和高度;您可以在线此处进行检查,并查看屏幕截图

We'll get proper width and height for both iFrames; you can check it online here and see the screenshot here.

这里描述的方法是 hack ,如果绝对必要并且没有其他方法可以使用; 对大多数动态生成的页面和SPA无效.该方法使用代理来获取页面HTML源代码,以绕过CORS策略( cors-anywhere 是创建简单的CORS代理服务器的简单方法,它具有在线演示https://cors-anywhere.herokuapp.com ),然后将JS代码注入该HTML以使用postMessage并将iFrame的大小发送至父文档.它甚至处理iFrame resize(与iFrame width: 100% 组合的)事件,并将iFrame大小发回给父对象.

The method described here is a hack and it should be used if it's absolutely necessary and there is no other way around; it won't work for most dynamic generated pages and SPAs. The method fetches the page HTML source code using a proxy to bypass CORS policy (cors-anywhere is an easy way to create a simple CORS proxy server and it has an online demo https://cors-anywhere.herokuapp.com) it then injects JS code to that HTML to use postMessage and send the size of the iFrame to the parent document. It even handles iFrame resize (combined with iFrame width: 100%) event and posts the iFrame size back to the parent.

用于修补iFrame HTML代码并注入自定义Javascript的函数,该函数将使用postMessageloadresize上将iFrame大小发送给父级.如果origin参数有一个值,则将使用该原始URL将HTML <base/>元素添加到头部,因此,诸如/some/resource/file.ext之类的HTML URI将被iFrame中的原始URL正确提取.

A function to patch the iFrame HTML code and inject custom Javascript that will use postMessage to send the iFrame size to the parent on load and on resize. If there is a value for the origin parameter, then an HTML <base/> element will be prepended to the head using that origin URL, thus, HTML URIs like /some/resource/file.ext will get fetched properly by the origin URL inside the 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:

如果设置了useProxy参数,则该函数用于获取使用代理绕过CORS的页面HTML.发送尺寸数据时,可以将其他参数传递给postMessage.

getIframeHtml:

A function to get a page HTML bypassing the CORS using a proxy if useProxy param is set. There can be additional parameters that will be passed to the postMessage when sending size data.

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

消息事件处理程序功能与相同来源iFrame大小" 中的功能完全相同.

The message event handler function is exactly the same as in "Same origin iFrame size".

我们现在可以在iFrame中加载跨源域,并注入我们的自定义JS代码:

We can now load a cross origin domain inside an iFrame with our custom JS code injected:

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

即使我们对iFrame主体使用overflow-y: auto(应该为overflow-y: hidden,也不会在滚动时出现闪烁),我们将iFrame调整到其内容全高的大小,甚至不进行任何垂直滚动).

And we'll get the iFrame to size to it's contents full height without any vertical scrolling even using overflow-y: auto for the iFrame body (it should be overflow-y: hidden so we don't get scrollbar flickering on resize).

您可以此处进行在线检查.

再次注意到 这是一个黑客行为 ,应 应避免 ;我们无法访问跨域 iFrame文档或注入任何东西.

Again to notice that this is a hack and it should be avoided; we cannot access Cross-Origin iFrame document nor inject any kind of things.

这篇关于如何在没有宽高比假设的情况下使iframe响应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆