是否有有效的跨域 iframe 高度自动调整器?

Posted

技术标签:

【中文标题】是否有有效的跨域 iframe 高度自动调整器?【英文标题】:Is there a cross-domain iframe height auto-resizer that works? 【发布时间】:2011-08-01 04:16:03 【问题描述】:

我尝试了一些解决方案,但都没有成功。我想知道是否有一个解决方案,最好有一个易于理解的教程。

【问题讨论】:

您尝试了哪些不成功的解决方案? 我试过这个,但它在 webkit 浏览器中吓坏了,如文章所述:css-tricks.com/cross-domain-iframe-resizing 还有另一个,但我不记得网址了。 css-tricks.com/cross-domain-iframe-resizing 这能回答你的问题吗? Adjust width and height of iframe to fit with content in it 【参考方案1】:

你有三个选择:

1。使用 iFrame-resizer

这是一个简单的库,用于根据内容调整 iFrame 的大小。它使用 PostMessage 和 MutationObserver API,并支持 IE8-10。它还具有内容页面的选项,可以请求包含的 iFrame 具有一定的大小,并且还可以在完成后关闭 iFrame。

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

2。使用 Easy XDM(PostMessage + Flash 组合)

Easy XDM 使用一系列技巧来实现多个浏览器中不同窗口之间的跨域通信,并且有使用它来调整 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 通过在现代浏览器上使用 PostMessage 和基于 Flash 的解决方案作为旧浏览器的后备来工作。

另见this thread on ***(还有其他,这是一个常见问题)。另外,Facebook would seem to use a similar approach。

3。通过服务器通信

另一种选择是将 iframe 高度发送到您的服务器,然后使用 JSONP 从父网页从该服务器轮询(或尽可能使用长轮询)。

【讨论】:

对于 PostMessage,如果您已经在使用 jQuery,您可能还想看看 Ben Alman 的精彩 postMessage 插件:benalman.com/projects/jquery-postmessage-plugin iFrame-resizer 需要访问托管 iframe 内容的服务器。因此,您只能将其用于您控制的域。 很好,@Hokascha。该项目声称支持跨域 iframe,但阅读文档后发现它仍然需要服务器访问嵌入式域。 没有这些“替代方案”是此问题的实际解决方案。提问者希望从他们无法控制的网站设置跨域 iFrame 的 iFrame 高度。 1.需要服务器访问。 2. 需要我认为很差的软件。 Easy XDM 就像 10 年前一样。从 2019 年开始的最新版本需要闪存。谢天谢地,Flash 已经死了,没有人应该依赖它。 3.需要服务器访问权限。【参考方案2】:

我得到了根据内容动态设置 iframe 高度的解决方案。这适用于跨域内容。 要实现这一点,需要遵循一些步骤。

    假设您在“abc.com/page”网页中添加了 iframe

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

    接下来要在网页“abc.com/page”下绑定windows“message”事件

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

在 iframe 加载时,您必须使用“FrameHeight”消息向 iframe 窗口内容发送消息:

function setIframeHeight(ifrm) 
   var height = ifrm.contentWindow.postMessage("FrameHeight", "*");   

    在 iframe 下添加的主页“xyz.pqr/contactpage”下,您必须绑定窗口“消息”事件,所有消息都将从“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,谢谢:)【参考方案3】:

我所做的是比较 iframe 的滚动宽度,直到它改变大小,同时我逐渐设置 IFrame 高度。它对我来说很好。您可以根据需要调整增量。

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

【讨论】:

您可能希望对循环数添加一个上限,否则“while”可能会退化为无限循环。 这让我的页面崩溃了。 很好的简单解决方案。但在我看来,尺寸增量受屏幕尺寸的限制。【参考方案4】:

我有一个脚本可以放入 iframe 的内容中。它还确保 iFrameResizer 存在(将其作为脚本注入),然后进行大小调整。

我将在下面插入一个简化的示例。

// /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/iframe-resizer@3.5.14/js/iframeResizer.contentWindow.min.js
        resizerScriptTag.src = 'https://unpkg.com/iframe-resizer@3.5.14/js/iframeResizer.min.js';
     else 
        // Cool, the iFrameResizer exists so we can just load our iframe.
        loadIFrame();
        
())

然后可以使用如下脚本将 iframe 内容注入另一个页面/站点中的任何位置:

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

iframe 内容将被注入到您放置脚本标签的下方。

希望这对某人有所帮助。 ?

【讨论】:

很好,我添加了&lt;script ... data-src="http://google.com"&gt; 并用它填充了 iframe src。 截至发稿,当前版本为iframe-resizer@4.2.10 ... 扩展为一个完整的可用示例codesandbox.io/s/remote-embed-ifrm-qdb74 @BananaAcid - 您的代码框链接不再有效 感谢提及,链接变成了:codesandbox.io/s/remote-embed-ifrm-yz0xl。 (注意:使用多个页面的代码和框“假货”可能会挂起。重新加载可能会有所帮助)【参考方案5】:

我找到了另一个使用 PHP 来获取 iframe 大小的 web 开发服务器端解决方案。

首先是使用服务器脚本 PHP 通过内部函数进行外部调用:(例如 file_get_contents,但带有 curl 和 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" margin margin 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>

需要在div(iframe_reserve)下显示函数调用生成的html,使用简单的echo curl_get_file_contents("location url iframe","activation proxy")

执行此操作后,使用 javascript 的 body 事件函数 onload 只需简单控制内容 div 即可获取页面 iframe 的高度 (iframe_reserve)

所以我使用divHeight = document.getElementById("iframe_reserve").clientHeight; 来获取外部页面的高度,我们将在屏蔽 div 容器 (iframe_reserve) 后调用。在此之后,我们加载了具有良好高度的 iframe。

【讨论】:

【参考方案6】:

我在工作时遇到了这个问题(使用 React)。基本上,我们有一些外部 html 内容,我们将这些内容保存到数据库中的文档表中,然后当您在 Documents 数据集中时在某些情况下插入到页面中。

因此,鉴于n 内联,其中高达n 可以包含外部html,我们需要设计一个系统来自动调整每个内联的iframe 大小,一旦内容完全加载到每个内联。在旋转了我的***之后,这就是我最终这样做的方式:

    在我们的 React 应用程序的索引中设置一个 message 事件侦听器,以检查我们将从发送方 iframe 设置的特定键。 在实际呈现 iframe 的组件中,在将外部 html 插入其中后,我附加了一个 &lt;script&gt; 标记,该标记将等待 iframe 的 window.onload 触发。一旦触发,我们使用postMessage 向父窗口发送一条消息,其中包含有关 iframe id、计算高度等的信息。 如果源匹配并且在索引侦听器中满足键,则获取我们在 MessageEvent 对象中传递的 iframe 的 DOM id 获得iframe 后,只需根据从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.heightpx`
            // 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.scrollHeightpx`
            
         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"
                )
            
        
    
/>

【讨论】:

【参考方案7】:

这是一个替代实现。

基本上,如果您能够在其他域中编辑页面,则可以放置另一个属于您的服务器的 iframe 页面,从而节省 cookie 的高度。 更新时使用间隔读取cookie,更新iframe的高度。仅此而已。

编辑:2019 年 12 月

上面的解决方案基本上在 iframe 内使用另一个 iframe 第三个 iframe 属于首页域,您使用将大小值保存到 cookie 的查询字符串调用此页面,外部页面以一定间隔检查此查询。但这不是一个好的解决方案,所以你应该遵循这个:

在首页:

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

您可以在这里查看m.origin,看看它来自哪里。

在框架页面中:

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

尽管如此,请不要忘记这不是那么安全的方式。使用您想要的值使其安全更新 * 值 (targetOrigin)。 请关注文档:https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

【讨论】:

这些链接现在已失效 (404),并且答案中没有总结内容。 :( 链接仍然无效。 404

以上是关于是否有有效的跨域 iframe 高度自动调整器?的主要内容,如果未能解决你的问题,请参考以下文章

iframe的跨域高度自适应(通过跨域页面中嵌套本域页面)

跨域 iframe 调整大小?

将 iframe 内容高度设置为动态自动调整大小

调整跨域 iframe 高度的大小 [重复]

iframe调用子页面按子页面的内容自动调整高度

如何在跨域中获取 iframe contentWindow 高度