根据内容调整 iframe 的大小

IT技术 javascript iframe widget
2021-01-11 02:46:39

我正在开发一个类似 iGoogle 的应用程序。来自其他应用程序(在其他域上)的内容使用 iframe 显示。

如何调整 iframe 的大小以适合 iframe 内容的高度?

我试图破译谷歌使用的 javascript,但它被混淆了,到目前为止,搜索网络一直没有结果。

更新:请注意内容是从其他域加载的,因此适用同源策略

6个回答

我们遇到了这种类型的问题,但与您的情况略有相反 - 我们向其他域上的站点提供 iframe 内容,因此同源策略也是一个问题。经过许多小时的 google 搜索,我们最终找到了一个(有点……)可行的解决方案,您可以根据自己的需要进行调整。

有一种绕过同源策略的方法,但是它需要在 iframe 内容和框架页面上进行更改,因此如果您没有能力在两侧请求更改,则此方法对您不会很有用,我耽心。

有一个浏览器的怪癖,它允许我们绕过同源策略 - javascript 可以与自己域中的页面通信,或者与它具有 iframe 的页面通信,但永远不会与它被框架化的页面通信,例如,如果您有:

 www.foo.com/home.html, which iframes
 |-> www.bar.net/framed.html, which iframes
     |-> www.foo.com/helper.html

然后home.html可以与framed.html(iframed)和helper.html(同一个域)通信。

 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可以向helper.html(iframed)发送消息,不能 home.html(子级不能与父级进行跨域通信)。

这里的关键是,helper.html能够接收来自消息framed.html,并且还可以沟通home.html

所以基本上,当framed.html加载时,它会计算出自己的高度,告诉helper.html,它将消息传递给home.html,然后可以调整framed.html坐下的 iframe 的大小

我们发现,从传递消息最简单的方法framed.htmlhelper.html是通过URL参数。要做到这一点,framed.html有一个src=''指定的 iframe 当它onload触发时,它会评估自己的高度,并将此时 iframe 的 src 设置为helper.html?height=N

这里有一个关于facebook如何处理它的解释,这可能比我上面的更清楚!


代码

在 中www.foo.com/home.html,需要以下 javascript 代码(这可以从任何域上的 .js 文件加载,顺便说一句..):

<script>
  // Resize iframe to full height
  function resizeIframe(height)
  {
    // "+60" is a general rule of thumb to allow for differences in
    // IE & and FF height reporting, can be adjusted as required..
    document.getElementById('frame_name_here').height = parseInt(height)+60;
  }
</script>
<iframe id='frame_name_here' src='http://www.bar.net/framed.html'></iframe>

www.bar.net/framed.html

<body onload="iframeResizePipe()">
<iframe id="helpframe" src='' height='0' width='0' frameborder='0'></iframe>

<script type="text/javascript">
  function iframeResizePipe()
  {
     // What's the page height?
     var height = document.body.scrollHeight;

     // Going to 'pipe' the data to the parent through the helpframe..
     var pipe = document.getElementById('helpframe');

     // Cachebuster a precaution here to stop browser caching interfering
     pipe.src = 'http://www.foo.com/helper.html?height='+height+'&cacheb='+Math.random();

  }
</script>

内容www.foo.com/helper.html

<html> 
<!-- 
This page is on the same domain as the parent, so can
communicate with it to order the iframe window resizing
to fit the content 
--> 
  <body onload="parentIframeResize()"> 
    <script> 
      // Tell the parent iframe what height the iframe needs to be
      function parentIframeResize()
      {
         var height = getParam('height');
         // This works as our parent's parent is on our domain..
         parent.parent.resizeIframe(height);
      }

      // Helper function, parse param from request string
      function getParam( name )
      {
        name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
        var regexS = "[\\?&]"+name+"=([^&#]*)";
        var regex = new RegExp( regexS );
        var results = regex.exec( window.location.href );
        if( results == null )
          return "";
        else
          return results[1];
      }
    </script> 
  </body> 
</html>
@Frederico,这种方法确实仍然有效,但需要修改解决方案才能在最新版本的 FF 和 Chrome 中工作。如果 iframe 高度大于正在呈现的页面,则滚动高度将不正确地使用 iframe 高度。这会导致 iframe 每次翻页时可能会增加 60 像素。需要的修复是在框架 HTML 中包含一个 ID 元素。获取它的滚动高度而不是文档,它就像一个冠军。
2021-03-13 02:46:39
天才!除了 Abdullah 的补充:我没有将 body onload 设置为 call parentIframeResize(),而是使用 JQuery 来捕获页面加载和任何调整大小的事件:$(document).ready(iframeResizePipe); $(window).resize(iframeResizePipe);这允许我设置 iframe width="100%",如果窗口宽度发生变化以进行文本换行或其他什么,内部框架将认识到它需要调整大小。
2021-03-14 02:46:39
我建议给 iframe id=frame_name_here 一个默认的高度属性,比如 height="2000"。这意味着当页面加载时,框架内容是可见的。当调整大小发生时,没有“闪烁”。调整大小在页面折叠下方缩小/扩展。如果您知道 iframe 内容的最大高度,则使用该值……这也会带来更好的 noscript 体验。
2021-03-15 02:46:39
很好,谢谢!我做了一些补充:使用 jQuery 获取 framed.html 中的主体高度(FF 问题,主体不断增长): var height = $(document.body).height(); 使用 body onload 事件在 home.html 中创建框架,类似于您在 framed.html 中的方法。解决了在 FF 和 Safari 中刷新时不更新框架的问题。
2021-03-25 02:46:39
不幸的是,这似乎不再起作用。我仔细地实现了这一点,但在 Chrome 控制台中收到以下 javascript 错误:Uncaught DOMException: Blocked a frame with origin " example.com " from accessing a cross-origin frame.
2021-03-28 02:46:39

如果你不需要处理来自不同域的 iframe 内容,试试这个代码,它会彻底解决问题,而且很简单:

<script language="JavaScript">
<!--
function autoResize(id){
    var newheight;
    var newwidth;

    if(document.getElementById){
        newheight=document.getElementById(id).contentWindow.document .body.scrollHeight;
        newwidth=document.getElementById(id).contentWindow.document .body.scrollWidth;
    }

    document.getElementById(id).height= (newheight) + "px";
    document.getElementById(id).width= (newwidth) + "px";
}
//-->
</script>

<iframe src="usagelogs/default.aspx" width="100%" height="200px" id="iframe1" marginheight="0" frameborder="0" onLoad="autoResize('iframe1');"></iframe>
将元素本身传递给函数以避免所有那些document.getElementById(id)s 并简化代码可能更理想
2021-03-18 02:46:39
这对我不起作用,因为 contentWindow.document.body.scrollHeight 保持 0 而不是实际高度。
2021-03-19 02:46:39
我不明白……你检查 document.getElementById 是否存在,然后在检查区域之外调用它?
2021-03-23 02:46:39
@Eugenio 和包括我在内的其他所有人,要解决最后一个问题:请参阅stackoverflow.com/questions/3053072/... 在您询问 iframe 内文档的高度之前,您应该将 iframe 对象的高度设置为“自动”。请注意,您应该使用 style.height 而不仅仅是上面的答案中的高度
2021-03-24 02:46:39
这个解决方案,和我尝试过的所有其他解决方案一样,只有在高度增加时才有效,如果从一个高页面导航到一个较短的页面,高度仍然根据第一个的高度设置。有没有人找到这个问题的解决方案?
2021-04-09 02:46:39

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

window.postMessage()

window.postMessage 是一种安全启用跨域通信的方法。通常,当且仅当执行它们的页面位于具有相同协议(通常都是 http)、端口号(80 是 http 的默认值)和主机(模document.domain 被两个页面设置为相同的值)。window.postMessage 提供了一种受控机制,以在正确使用时安全的方式规避此限制。

概括

window.postMessage,当调用时,会导致在任何必须执行的挂起脚本完成时在目标窗口分派 MessageEvent(例如,如果 window.postMessage 从事件处理程序中调用,则剩余的事件处理程序,先前设置的挂起超时等。 )。MessageEvent 有类型消息,一个数据属性,它被设置为提供给 window.postMessage 的第一个参数的字符串值,一个 origin 属性对应于在时间窗口调用 window.postMessage 的窗口中的主文档的来源。 postMessage 被调用,源属性是调用 window.postMessage 的窗口。(事件的其他标准属性与其预期值一起存在。)

iframe的调整器库使用的postMessage保持一个iFrame尺寸为它的内容,随着MutationObserver检测改动的内容和不依赖于jQuery的。

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

jQuery:跨域脚本的优点

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

有调整 iframe 窗口大小的演示...

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

这篇文章展示了如何移除对 jQuery 的依赖... Plus 有很多有用的信息和其他解决方案的链接。

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

准系统示例...

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

window.postMessage 上的 HTML 5 工作草案

http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages

John Resig 谈跨窗口消息传递

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

periscopedata 正在使用 postMessage,如果它对他们来说足够好,那么对我们来说也足够了。这是他们的文档:doc.periscopedata.com/docv2/embed-api-options
2021-03-14 02:46:39
很高兴看到您回答问题而不是解释有助于解决方案的辅助技术。
2021-03-19 02:46:39
postMessage 似乎是 X 浏览器。只有 IE 需要关注 bug 或事情: 1. 仅在框架或 iframe 之间进行通信 2. 只能发布字符串。请参阅:caniuse.com/x-doc-messaging
2021-03-24 02:46:39
postMessage 本身不是跨浏览器的,但 jQuery 插件似乎在必要时可以很好地伪造它。唯一真正的问题是在不支持 postMessage 的情况下添加到 URL 栏中的“垃圾”。
2021-04-03 02:46:39

使用 jQuery 的最简单方法:

$("iframe")
.attr({"scrolling": "no", "src":"http://www.someotherlink.com/"})
.load(function() {
    $(this).css("height", $(this).contents().height() + "px");
});
未按要求跨域。
2021-04-07 02:46:39

最后,我找到了一些其他解决方案,可以使用window.postMessage(message, targetOrigin);. 我在这里解释我是如何做到的。

站点 A = http://foo.com 站点 B = http://bar.com

SiteB 正在加载 siteA 网站内

SiteB网站有这条线

window.parent.postMessage("Hello From IFrame", "*"); 

或者

window.parent.postMessage("Hello From IFrame", "http://foo.com");

然后站点A有以下代码

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

如果你想添加安全连接,你可以使用这个 if 条件 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.
}

浏览器

IFrame内部:

 window.parent.postMessage('{"key":"value"}','*');

外部:

 eventer(messageEvent, function (e) {
   var data = jQuery.parseJSON(e.data);
   doSomething(data.key);
 }, false);
这个解决了我的情况。我不得不说这可能只是因为 iFrame 提供程序正在使用正确的信息(帧高度,在我的情况下)调度事件。看看它是否适用于任何 iFrame 会很有趣。谢谢@Selvamani
2021-03-14 02:46:39