跨域 iframe 调整大小

IT技术 javascript html css iframe cross-domain
2021-01-17 04:53:43

如何从另一个域调整 iframe 的大小

-编辑

向下滚动一些解决方案......或阅读如何不这样做:D

经过数小时的代码黑客攻击 - 结论是 iframe 中的任何内容都无法访问,即使是在我的域上呈现的滚动条。我尝试了很多技术都无济于事。

为了节省您的时间,甚至不要走这条路,只需使用 sendMessages 进行跨域通信。 我使用了 HTML < 5 的插件 - 转到底部的一个很好的例子:)


过去几天我一直在尝试将 iframe 集成到站点中。这是一个短期解决方案,而另一方开发和 API(可能需要几个月......)而且因为这是短期解决方案,我们确实想使用 easyXDM-我可以访问另一个域,但要求他们这样做已经够困难了按原样添加 p3p 标头.....

3 个 iframe

我找到的最接近的解决方案是 3 个 iframe - 但它对 chrome 和 safari 很敏感,所以我不能使用它。

在 chrome 中打开

http://css-tricks.com/examples/iFrameResize/crossdomain.php#frameId=frame-one&height=1179

测量滚动条

我发现了另一篇关于如何使用滚动高度尝试调整表单大小的帖子..理论上它运行良好,但我无法使用 iframes 滚动高度正确应用它..

document.body.scrollHeight

显然使用主体高度(无法访问这些属性 100% 是基于客户端显示的画布而不是 x 域文档高度)

我尝试使用 jquery 来获取 iframes 高度

$('#frameId').Height()

$('#frameId').clientHeight

$('#frameId').scrollHeight

在 chrome 和 ie 中返回不同的值 - 或者根本没有意义。问题是框​​架内的所有内容都被拒绝了 - 甚至滚动条......

计算样式

但是,如果我在 iframe 的 chrome 中检查和元素,它会向我显示 iframe 内的文档尺寸(使用 jquery x-domain 获取 iframe.heigh - 访问被拒绝)计算的 CSS 中没有任何内容 在此处输入图片说明

现在 chrome 是如何计算的?(编辑 - 浏览器使用其内置渲染引擎重新渲染页面以计算所有这些设置 - 但未附加到任何地方以防止跨域欺诈......所以......)

HTML4

我阅读了 HTML4.x 的规范,它说应该有通过 document.element 公开的只读值,但它通过 jquery 拒绝访问

代理帧

我沿着代理站点的路线返回并计算哪个好..直到用户通过 iframe 登录并且代理获取登录页面而不是实际内容。还有一些人两次调用页面是不可接受的

http://www.codeproject.com/KB/aspnet/asproxy.aspx

http://www.johnchapman.name/aspnet-proxy-page-cross-domain-requests-from-ajax-and-javascript/

重新渲染页面

我没有走这么远,但是有 jscript 引擎可以查看源文件并根据源文件重新呈现页面。但它需要破解那些 jscripts ......这对于商业实体来说不是一个理想的情况......还有一些涉及纯 Java 小程序或服务器端渲染

http://en.wikipedia.org/wiki/Server-side_JavaScript

http://htmlunit.sourceforge.net/ <-java 不是 jscript

http://maxq.tigris.org/


编辑 09-2013 更新

所有这些都可以通过 HTML5 套接字完成。但对于非 HTML5 投诉页面,easyXDM 是很好的后备方案。

解决方案 1非常好的解决方案!

使用easyXDM

在您的服务器上,您以以下形式设置页面

<html>
<head>
<script src="scripts/easyXDM.js" type="text/javascript"></script>
<script type="text/javascript" language="javascript">

    var transport = new easyXDM.Socket(/** The configuration */{
    remote: "http://www.OTHERDOMAIN.com/resize_intermediate.html?url=testpages/resized_iframe_1.html",

    //ID of the element to attach the inline frame to
    container: "embedded",
    onMessage: function (message, origin) {
        var settings = message.split(",");
        //Use jquery on a masterpage.
        //$('iframe').height(settings[0]);
        //$('iframe').width(settings[1]);

        //The normal solution without jquery if not using any complex pages (default)
        this.container.getElementsByTagName("iframe")[0].style.height = settings[0];
        this.container.getElementsByTagName("iframe")[0].style.width = settings[1];
    }
});

</script>

</head>

<body>
    <div id="embedded"></div>
</body>

在调用者域上,他们只需要在同一个地方添加 intermediate_frame html 和 easyXDM.js。就像父文件夹一样 - 然后您可以访问相关目录或只为您包含的文件夹。

选项1

如果您不想向所有页面添加脚本,请查看选项 2!

然后他们可以在需要调整大小的每个页面的末尾添加一个简单的 jscript。无需在每个页面中都包含 easyxdm。

 <script type="text/javascript">
            window.onload = function(){ parent.socket.postMessage( (parseInt(document.body.clientHeight)) + "," + ( document.body.clientWidth ) );  };
        </script>

我修改了它发送的参数。如果您希望宽度正常工作,则otherdomain 上的页面需要在样式中包含页面的宽度,该样式看起来类似于:

<style type="text/css">
            html, body {
                overflow: hidden;
                margin: 0px;
                padding: 0px;
                background-color: rgb(75,0,85);
                color:white;
                width:660px
            }
            a {
            color:white;
            visited:white;
            }
        </style>

这对我很有用。如果不包括宽度,那么框架的行为会有点奇怪,并且有点试图猜测它应该是什么......并且如果你需要它不会缩小。

选项 2

修改中间帧以轮询更改

你的中间框架应该看起来像这样..

    <!doctype html>
<html>
    <head>

        <title>Frame</title>
        <script type="text/javascript" src="easyXDM.js">
        </script>
        <script type="text/javascript">
            var iframe;
            var socket = new easyXDM.Socket({
                //This is a fallback- not needed in many cases
                swf: "easyxdm.swf",
                onReady: function(){
                    iframe = document.createElement("iframe");
                    iframe.frameBorder = 0;
                    document.body.appendChild(iframe);
                    iframe.src = "THE HOST FRAME";
            iframe.onchange = messageBack();

                },
                onMessage: function(url, origin){
                    iframe.src = url;
                }
            });

            //Probe child.frame for dimensions.
            function messageBack(){
                socket.postMessage ( iframe.contentDocument.body.clientHeight + "," + iframe.contentDocument.body.clientWidth); 
            };

            //Poll for changes on children every 500ms.
            setInterval("messageBack()",500); 

        </script>
        <style type="text/css">
            html, body {
                overflow: hidden;
                margin: 0px;
                padding: 0px;
                width: 100%;
                height: 100%;
            }

            iframe {
                width: 100%;
                height: 100%;
                border: 0px;
            }
        </style>
    </head>
    <body>

    </body>
</html>

如果大小发生变化,则可以更有效地检查间隔,并且仅在尺寸发生变化时才发送,而不是每 500 毫秒发送一次消息。如果您实施此检查,那么您可以将轮询更改为低至 50 毫秒!玩得开心


跨浏览器工作并且速度很快。强大的调试功能!!

Excellent Work to  Sean Kinsey  who made the script!!!

解决方案 2(有效但不是很好)

所以基本上,如果您与其他域有相互协议,那么您可以添加一个库来处理 sendmessage。如果您没有任何访问其他域的权限.. 继续寻找更多黑客 - 因为我找不到或完全证明我发现的这些。

所以另一个域将在那里包含这些 Head 标签

<script src="scripts/jquery-1.5.2.min.js" type="text/javascript"></script>
<script src="scripts/jquery.postmessage.min.js" type="text/javascript"></script>
<script src="scripts/club.js" type="text/javascript"></script>

在 club.js 中只是我为调整大小调用所做的一些自定义调用,并且包含..

 $(document).ready(function () {   
    var parent_url = decodeURIComponent( document.location.hash.replace( /^#/, '' ) ),link;
//Add source url hash to each url to authorise callback when navigating inside the frame.Without this clicking any links will break the communication and no messages will be received
$('a[href]').each(function(){ 
     this.href = this.href + document.location.hash ;
});
//Get the dimensions and send back to calling page.
var h1 = document.body.scrollHeight;
var w1 = document.body.scrollWidth;
$.postMessage({ if_height: h1, if_width: w1 }, parent_url, parent );
  });

您的页面将完成所有艰苦的工作,并有一个不错的脚本...

  //This is almost like request.querystring used to get the iframe data
  function querySt(param, e) {
         gy = e.split("&");
         for (i = 0; i < gy.length; i++) {
             ft = gy[i].split("=");
             if (ft[0] == param) {
                 return ft[1];
             }
         }
     }


     $(function () {
         // Keep track of the iframe dimensions.
         var if_height;
         var if_width;
         // Pass the parent page URL into the Iframe in a meaningful way (this URL could be
         // passed via query string or hard coded into the child page, it depends on your needs).
         src = 'http://www.OTHERDOAMIN.co.uk/OTHERSTARTPAGE.htm' + '#' + encodeURIComponent(document.location.href),
         // Append the Iframe into the DOM.
         iframe = $('<iframe " src="' + src + '" width="100%" height="100%" scrolling="no" frameborder="0"><\/iframe>').appendTo('#iframe');

         // Setup a callback to handle the dispatched MessageEvent event. In cases where
         // window.postMessage is supported, the passed event will have .data, .origin and
         // .source properties. Otherwise, this will only have the .data property.
         $.receiveMessage(function (e) {
             // Get the height from the passsed data.
             //var h = Number(e.data.replace(/.*if_height=(\d+)(?:&|$)/, '$1'));
             var h = querySt("if_height", e.data);
             var w = querySt("if_width", e.data);


             if (!isNaN(h) && h > 0 && h !== if_height) {
                 // Height has changed, update the iframe.
                 iframe.height(if_height = h);
             }
             if (!isNaN(w) && w > 0 && w !== if_width) {
                 // Height has changed, update the iframe.
                 iframe.width(if_width = w);
             }
             //For debugging only really- can remove the next line if you want
             $('body').prepend("Recieved" + h + "hX" + w + "w .. ");
             // An optional origin URL (Ignored where window.postMessage is unsupported).
           //Here you must put the other domain.com name only! This is like an authentication to prevent spoofing and xss attacks! 
         }, 'http://www.OTHERDOMAIN.co.uk');
     });

选项 3

他们现在是一个用于管理跨域 iFrame 大小调整的小型 JS 库,它仍然需要 iFrame 中包含一些 JavaScript,但是,这只是 2.8k(765 字节 Gzipped)的原生 JS,没有任何依赖项并且在被父页面调用之前它不会做任何事情。这意味着它是其他人系统上的好客。

此代码使用 mutationObserver 来检测 DOM 更改并查看调整大小事件,以便 iFrame 保持与内容一致的大小。适用于 IE8+。

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

6个回答

与 Sean 提到的类似,您可以使用 postMessage。我花了很多时间尝试不同的方法来跨域调整 iframe 的大小,但没有运气,直到我偶然发现了 David Walsh 的这篇很棒的博客文章:http : //davidwalsh.name/window-iframe

这是我的代码和大卫的解决方案的组合。我的解决方案专门针对调整 iframe 的大小。

在子页面中,找到页面的高度并将其传递给父页面(其中包含 iframe)。将 element_id 替换为您的元素 id(html、body 等)。

<script>
function adjust_iframe_height(){
    var actual_height = document.getElementById('element_id).scrollHeight;
    parent.postMessage(actual_height,"*"); 
    //* allows this to post to any parent iframe regardless of domain
}
</script>

<body onload="adjust_iframe_height();"> 
//call the function above after the content of the child loads

在父窗口上,添加此代码。将 iframe_id 替换为您的 iframe ID:

<script>
// Create IE + others compatible event handler
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

// Listen to message from child window
eventer(messageEvent,function(e) {
  console.log('parent received message!:  ',e.data);
  document.getElementById('iframe_id').height = e.data + 'px';
},false);
</script>

如果您打开控制台,您将看到控制台日志中打印的高度。这将帮助您进行调试,这就是我将它留在那里的原因。

最好的,贝克

这很好用,但在 IE 中没有那么多。我不得不发送 2 个变量,但如果它是一个数组或对象,IE 将不接受“数据”。但是,我只是将数据创建为 data = var1 + "|" 变量 2。在父级上,您只需将字符串拆分为 | 并取回您的 2 个变量。
2021-04-09 04:53:43

问题是 - 除了使用跨域消息传递之外别无他法,因为您需要将计算高度从一个域中的文档获取到另一个域中的文档。

因此,要么使用postMessage(适用于所有现代浏览器)执行此操作,要么花费 5 分钟调整来自 easyXDM调整大小 iframe 示例

对方真的只需要将几个文件复制到他们的域中,并在他们的文档中添加一行代码即可。

是的 - 如果不出意外,您至少已经尝试过,实验过,并且可能还学到了一些新东西:)
2021-03-18 04:53:43
是的...我使用了 sendmessage jquwery 插件 - 虽然我发现了另一个黑客 - 误报.. ;) 谢谢你的好提示!
2021-03-19 04:53:43
相反,我的朋友。我找到了一种将 iframe 从跨域页面调整为 iframes 文档大小并且没有滚动条的方法。它有点hacky,但稍后会发布......它是如此简单,令人难以置信。
2021-03-20 04:53:43
想象一下,如果我做到了——纯粹是命中注定——会太棒了。但是是的 - 苗条 :D 谢谢
2021-03-20 04:53:43
好吧,有时最好的办法是听取同行的意见-您发现其他人没有的魔术技巧的可能性很小:)
2021-04-08 04:53:43

在查看了许多不同的解决方案之后,我最终编写了一个简单的小型库来考虑许多不同的用例。因为我需要一个解决方案,在门户页面上支持多个用户生成的 iFrame,支持浏览器调整大小,并且可以处理 iFrame 之后的主机页面 JavaScript 加载。我还添加了对调整宽度和回调函数的支持,并允许覆盖 body.margin,因为您可能希望将此设置为零。

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

iframe 代码只是一些独立的 JavaScript,因此它是其他人页面上的好客。

然后使用以下可用选项在主机页面上初始化脚本。

iFrameResize({
    log                    : true,  // For development
    autoResize             : true,  // Trigering resize on events in iFrame
    contentWindowBodyMargin: 8,     // Set the default browser body margin style (in px)
    doHeight               : true,  // Calculates dynamic height
    doWidth                : false, // Calculates dynamic width
    enablePublicMethods    : true,  // Enable methods within iframe hosted page 
    interval               : 32,    // interval in ms to recalculate body height, 0 to disable refreshing
    scrolling              : false, // Enable the scrollbars in the iFrame
    callback               : function(messageData){ // Callback fn when message is received
        $('p#callback').html(
            '<b>Frame ID:</b> '    + messageData.iframe.id +
            ' <b>Height:</b> '     + messageData.height +
            ' <b>Width:</b> '      + messageData.width + 
            ' <b>Event type:</b> ' + messageData.type
        );
    }
});
不错的一个哥们 绝对会被很多人使用的东西!+1
2021-03-18 04:53:43
如果我无权访问远程域,该怎么办?谢谢
2021-04-05 04:53:43
@BalaPeterson,如果您无法访问远程域,则由于浏览器中的安全限制,您无法执行此操作。
2021-04-08 04:53:43

我没有在 iframe 上使用 scroll=no,而是将其更改为“自动”。然后,我得到实际窗口的大小

$(window).height();

并将其用作 iframe 高度属性。

嗯,结果是……

我们永远不会有“页面”滚动,只有“iframe”滚动。当你导航时,谁是卷轴并不重要,但重要的是只有 1 个。

嗯,存在的问题是用户在导航时只需调整窗口大小。为了解决这个问题,我使用:

setInterval(getDocHeight, 1);

您是否认为该解决方案会导致任何错误?它对我有用,在 iFrame 上我有由 php 生成的动态内容。我害怕未来的错误......

当然,“iframe”滚动始终与页面大小相同。因此,在导航时,您只会看到它是“页面”滚动。
2021-04-04 04:53:43

现在我只知道4种解决方案:

只有第三个才能解决很多问题。例如,您可以创建响应式 iFrame从内部关闭它,或者您可以与它通信但要做到这一点,您需要 Iframe 中的 iFrame 和“第三方 cookie”解决方法(可选)。

我已经用示例创建了一篇关于它的文章:事件驱动的跨域 iFrame