根据内容调整iframe大小

时间:2008-09-30 14:05:12

标签: javascript iframe widget

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

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

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

更新:请注意,内容是从其他域加载的,因此适用same-origin policy

23 个答案:

答案 0 :(得分:576)

我们遇到了这类问题,但与您的情况略有相反 - 我们向其他域中的网站提供了iframed内容,因此same origin policy也是一个问题。经过长达数小时的拖网搜索后,我们最终找到了一个(有点......)可行的解决方案,您可以根据自己的需要进行调整。

围绕相同的原始政策有一种方法,但它需要对iframed内容和框架页面进行更改,因此如果您无法双方请求更改,则此方法不会非常有用对你而言,我很害怕。

有一个浏览器怪癖允许我们绕过相同的原始政策 - javascript可以与其自己的域上的页面进行通信,也可以与已经有iframed的页面进行通信,但从不与其进行框架化的页面进行通信,例如:如果你有:

 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.html 1}}坐着。

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

There's an explanation here知道如何处理它,这可能比我的上面稍微清楚一点!

<小时/> 的代码

<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> 中,需要以下javascript代码(可以从任何域上的.js文件加载,顺便提一下......):

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> 的内容:

{{1}}

答案 1 :(得分:80)

如果您不需要处理来自其他域的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>

答案 2 :(得分:40)

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具有类型消息,一个data属性,设置为提供给window.postMessage的第一个参数的字符串值,一个origin属性,对应于窗口调用window.postMessage时窗口中主文档的原点。调用了postMessage,并调用了一个source属性,该属性是调用window.postMessage的窗口。 (事件的其他标准属性与其预期值一起出现。)

iFrame-Resizer 库使用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/

Barebones示例......

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/

答案 3 :(得分:9)

使用jQuery的最简单方法:

$("iframe")
.attr({"scrolling": "no", "src":"http://www.someotherlink.com/"})
.load(function() {
    $(this).css("height", $(this).contents().height() + "px");
});

答案 4 :(得分:6)

http://www.phinesolutions.com/use-jquery-to-adjust-the-iframe-height.html上的解决方案效果很好(使用jQuery):

<script type=”text/javascript”>
  $(document).ready(function() {
    var theFrame = $(”#iFrameToAdjust”, parent.document.body);
    theFrame.height($(document.body).height() + 30);
  });
</script>

我不知道你需要增加30个长度...... 1对我有用。

FYI :如果您的iFrame上已经有“height”属性,则只需添加style =“height:xxx”。这可能不是你想要的。

答案 5 :(得分:4)

可能有点晚了,因为所有其他答案都比较旧:-)但是......这是我的解决方案。在实际的FF,Chrome和Safari 5.0中进行了测试。

的CSS:

iframe {border:0; overflow:hidden;}

的javascript:

$(document).ready(function(){
    $("iframe").load( function () {
        var c = (this.contentWindow || this.contentDocument);
        if (c.document) d = c.document;
        var ih = $(d).outerHeight();
        var iw = $(d).outerWidth();
        $(this).css({
            height: ih,
            width: iw
        });
    });
});

希望这对任何人都有帮助。

答案 6 :(得分:3)

最后,我找到了一些其他解决方案,可以使用window.postMessage(message, targetOrigin);从iframe向父网站发送数据。在这里,我解释我是如何做的。

网站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");

然后siteA有以下代码

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

如果您想添加安全连接,可以在eventer(messageEvent, function (e) {})

中使用此if条件
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.
}

适用于IE

内部IFrame:

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

外:

 eventer(messageEvent, function (e) {
   var data = jQuery.parseJSON(e.data);
   doSomething(data.key);
 }, false);

答案 7 :(得分:2)

这是一个使用动态生成的样式表的简单解决方案,该样式表由与iframe内容相同的服务器提供。很简单,样式表“知道”iframe中的内容,并且知道用于设置iframe样式的尺寸。这绕过了相同的原始政策限制。

http://www.8degrees.co.nz/2010/06/09/dynamically-resize-an-iframe-depending-on-its-content/

所以提供的iframe代码会有一个伴随的样式表,如此......

<link href="http://your.site/path/to/css?contents_id=1234&dom_id=iframe_widget" rel="stylesheet" type="text/css" />
 <iframe id="iframe_widget" src="http://your.site/path/to/content?content_id=1234" frameborder="0" width="100%" scrolling="no"></iframe>

这确实需要服务器端逻辑能够计算iframe的呈现内容的维度。

答案 8 :(得分:2)

此答案仅适用于使用Bootstrap的网站。 Bootstrap的响应式嵌入功能完成了这项工作。它基于内容的宽度(而不是高度)。

<!-- 16:9 aspect ratio -->
<div class="embed-responsive embed-responsive-16by9">
  <iframe class="embed-responsive-item" src="http://www.youtube.com/embed/WsFWhL4Y84Y"></iframe>
</div>

jsfiddle:http://jsfiddle.net/00qggsjj/2/

http://getbootstrap.com/components/#responsive-embed

答案 9 :(得分:1)

我正在实施ConroyP的框架式解决方案,以替换基于设置document.domain的解决方案,但发现在不同的浏览器中正确确定iframe内容的高度非常困难(使用FF11,Ch17和IE9现在。)

ConroyP使用:

var height = document.body.scrollHeight;

但这仅适用于初始页面加载。我的iframe有动态内容,我需要在某些事件上调整iframe的大小。

我最终做的是为不同的浏览器使用不同的JS属性。

function getDim () {
    var body = document.body,
        html = document.documentElement;

    var bc = body.clientHeight;
    var bo = body.offsetHeight;
    var bs = body.scrollHeight;
    var hc = html.clientHeight;
    var ho = html.offsetHeight;
    var hs = html.scrollHeight;

    var h = Math.max(bc, bo, bs, hc, hs, ho);

    var bd = getBrowserData();

    // Select height property to use depending on browser
    if (bd.isGecko) {
        // FF 11
        h = hc;
    } else if (bd.isChrome) {
        // CH 17
        h = hc;
    } else if (bd.isIE) {
        // IE 9
        h = bs;
    }

    return h;
}

getBrowserData()是Ext Core的http://docs.sencha.com/core/source/Ext.html#method-Ext-apply

启发的浏览器检测功能

这对于FF和IE效果很好,但后来Chrome出现了问题。其中一个是时间问题,显然需要Chrome一段时间来设置/检测iframe的高度。如果iframe高于内容,则Chrome也不会正确返回iframe中内容的高度。当高度降低时,这不适用于动态内容。

要解决这个问题,我总是先将iframe设置为较低的高度,然后再检测内容的高度,然后将iframe高度设置为正确的值。

function resize () {
    // Reset the iframes height to a low value.
    // Otherwise Chrome won't detect the content height of the iframe.
    setIframeHeight(150);

    // Delay getting the dimensions because Chrome needs
    // a few moments to get the correct height.
    setTimeout("getDimAndResize()", 100);
}

代码未经过优化,而是来自我的开发测试:)

希望有人觉得这很有用!

答案 10 :(得分:1)

<html>
<head>
<script>
function frameSize(id){
var frameHeight;

document.getElementById(id).height=0 + "px";
if(document.getElementById){
    newheight=document.getElementById(id).contentWindow.document.body.scrollHeight;    
}

document.getElementById(id).height= (frameHeight) + "px";
}
</script>
</head>

<body>

<iframe id="frame"  src="startframe.html" frameborder="0" marginheight="0" hspace=20     width="100%" 

onload="javascript:frameSize('frame');">

<p>This will work, but you need to host it on an http server, you can do it locally.    </p>
</body>
</html>

答案 11 :(得分:0)

David Bradshaw 和 Chris Jacob 已经建议使用 postMessage 方法。我完全同意,这是做这些事情的正确方式。

我只想发布一个例子,真正有效的代码,以防万一它会成为一些现成的答案。

在 iframe 端:

<body onload="docResizePipe()">
<script>
var v = 0;
const docResizeObserver = new ResizeObserver(() => {
    docResizePipe();
});
docResizeObserver.observe(document.querySelector("body"));
function docResizePipe() {
    v += 1;
    if (v > 5) {
        return;
    }
    var w = document.body.scrollWidth;
    var h = document.body.scrollHeight;
    window.parent.postMessage([w,h], "*");
}
setInterval(function() {
    v -= 1;
    if (v < 0) {
        v = 0;
    }
}, 300);
</script>

注意递归阻塞机制——这是必要的,因为显然 Firefox 中存在一个错误,但无论如何让它存在。

在父文档端:

<iframe id="rpa-frame" src="3.html" style="border: none;"></iframe>
<script>
var rpaFrame = document.getElementById("rpa-frame");

window.addEventListener("message", (event) => {
    var width = event.data[0];
    var height = event.data[1];
    rpaFrame.width = parseInt(width)+60;
    rpaFrame.height = parseInt(height)+60;
    console.log(event);
}, false);
</script>

希望有用。

答案 12 :(得分:0)

找不到可以完美处理大文本+大图像的东西,但我最终得到了这个,似乎每次都正确或几乎正确:

    iframe.addEventListener("load",function(){
        // inlineSize, length, perspectiveOrigin, width
        let heightMax = 0;
        // this seems to work best with images...
        heightMax = Math.max(heightMax,iframe.contentWindow.getComputedStyle(iframe.contentWindow.document.body).perspectiveOrigin.split("px")[0]);
        // this seems to work best with text...
        heightMax = Math.max(heightMax,iframe.contentWindow.document.body.scrollHeight);
        // some large 1920x1080 images always gets a little bit off on firefox =/
        const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
        if(isFirefox && heightMax >= 900){
            // grrr..
            heightMax = heightMax + 100;
        }

        iframe.style.height = heightMax+"px";
        //console.log(heightMax);
    });

答案 13 :(得分:0)

这是一个老话题,但是到2020年,它仍然是一个相关的问题。我实际上也已将此答案发布在另一个旧线程中^^(https://stackoverflow.com/a/64110252/4383587


只想分享我的解决方案和兴奋。我花了整整四天的时间进行深入的研究和失败,但是我认为我找到了一种使iframe完全响应的巧妙方法!是的!

我尝试了很多不同的方法...我不想像postMessage那样使用双向通讯隧道,因为它对于相同来源的代码来说很尴尬,而对于跨来源的代码来说却很复杂(因为没有管理员想要打开门并代表您实施此操作。

我尝试使用MutationObservers,但仍然需要几个EventListener(调整大小,单击..),以确保正确处理布局的每个更改。 -如果脚本切换元素的可见性怎么办?还是动态地按需预加载更多内容呢? -另一个问题是从某处获取iframe内容的准确高度。大多数人建议使用scrollHeightoffsetHeight,或通过使用Math.max结合使用。问题是,直到iframe元素更改其尺寸后,这些值才会更新。为此,您可以在抓取iframe.height = 0之前简单地重设scrollHeight,但是还有更多警告。所以,拧这个。

然后,我有了另一个想法来尝试requestAnimationFrame,以摆脱我的事件和旁观者的地狱。现在,我可以立即对每个布局更改做出反应,但是我仍然没有可靠的来源来推断iframe的内容高度。然后我偶然发现了getComputedStyle!这是一个启示!一切都点击了。

好吧,看看我最终可以从无数次尝试中提取的代码。

function fit() {
    var iframes = document.querySelectorAll("iframe.gh-fit")

    for(var id = 0; id < iframes.length; id++) {
        var win = iframes[id].contentWindow
        var doc = win.document
        var html = doc.documentElement
        var body = doc.body
        var ifrm = iframes[id] // or win.frameElement

        if(body) {
            body.style.overflowX = "scroll" // scrollbar-jitter fix
            body.style.overflowY = "hidden"
        }
        if(html) {
            html.style.overflowX = "scroll" // scrollbar-jitter fix
            html.style.overflowY = "hidden"
            var style = win.getComputedStyle(html)
            ifrm.width = parseInt(style.getPropertyValue("width")) // round value
            ifrm.height = parseInt(style.getPropertyValue("height"))
        }
    }

    requestAnimationFrame(fit)
}

addEventListener("load", requestAnimationFrame.bind(this, fit))

就是这样,是的! -在HTML代码中输入<iframe src="page.html" class="gh-fit gh-fullwidth"></iframe>gh-fit是一个伪造的CSS类,用于标识脚本中应该影响DOM中的哪些iframe元素。 gh-fullwidth是带有一个规则width: 100%;的简单CSS类。

上面的脚本会自动从DOM中获取分配了.gh-fit类的所有iframe。然后,它从document.getComputedStyle(iframe)获取并使用预先计算的宽度和高度样式值,该值始终包含该元素的像素完美尺寸!太完美了!

请注意,此解决方案无法跨域使用(没有像IFrameResizer这样的双向通信策略,任何其他解决方案也不会)。 JS根本无法访问iframe的DOM,如果它不属于您。

我能想到的唯一其他跨域解决方案是使用诸如https://github.com/gnuns/allorigins之类的代理。但这将涉及深度复制您提出的每个请求-换句话说-您“窃取”整个页面源代码(使其成为您的代码,并让JS访问DOM),并对该源中的每个链接/路径进行修补,以便它也通过代理。重新链接例程很困难,但是可行。

我可能会尝试解决这个跨源问题,但这是另一天的事情。享受代码! :)

答案 14 :(得分:0)

使用jQuery:

parent.html

<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<style>
iframe {
    width: 100%;
    border: 1px solid black;
}
</style>
<script>
function foo(w, h) {
    $("iframe").css({width: w, height: h});
    return true;  // for debug purposes
}
</script>
<iframe src="child.html"></iframe>
</body>

child.html

<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function() {
    var w = $("#container").css("width");
    var h = $("#container").css("height");

    var req = parent.foo(w, h);
    console.log(req); // for debug purposes
});
</script>
<style>
body, html {
    margin: 0;
}
#container {
    width: 500px;
    height: 500px;
    background-color: red;
}
</style>
<div id="container"></div>
</body>

答案 15 :(得分:0)

在加载时使用jquery(跨浏览器):

 <iframe src="your_url" marginwidth="0"  marginheight="0" scrolling="No" frameborder="0"  hspace="0" vspace="0" id="containiframe" onload="loaderIframe();" height="100%"  width="100%"></iframe>

function loaderIframe(){
var heightIframe = $('#containiframe').contents().find('body').height();
$('#frame').css("height", heightFrame);
 }  

在响应式页面中调整大小:

$(window).resize(function(){
if($('#containiframe').length !== 0) {
var heightIframe = $('#containiframe').contents().find('body').height();
 $('#frame').css("height", heightFrame);
}
});

答案 16 :(得分:0)

获取iframe内容高度,然后将其提供给此iframe

 var iframes = document.getElementsByTagName("iframe");
 for(var i = 0, len = iframes.length; i<len; i++){
      window.frames[i].onload = function(_i){
           return function(){
                     iframes[_i].style.height = window.frames[_i].document.body.scrollHeight + "px";
                     }
      }(i);
 }

答案 17 :(得分:0)

这是一个jQuery方法,通过iframe的src属性在json中添加信息。这是一个演示,调整大小并滚动这个窗口..带有json的结果url看起来像这样...... http://fiddle.jshell.net/zippyskippy/RJN3G/show/#{docHeight:5124,windowHeight:1019,scrollHeight:571}#

这是源代码小提琴http://jsfiddle.net/zippyskippy/RJN3G/

function updateLocation(){

    var loc = window.location.href;
    window.location.href = loc.replace(/#{.*}#/,"") 
        + "#{docHeight:"+$(document).height() 
        + ",windowHeight:"+$(window).height()
        + ",scrollHeight:"+$(window).scrollTop()
        +"}#";

};

//setInterval(updateLocation,500);

$(window).resize(updateLocation);
$(window).scroll(updateLocation);

答案 18 :(得分:0)

如果要缩小网页以使其适合iframe尺寸:

  1. 您应调整 iframe 的大小以使其适合内容
  2. 然后你应该用加载的网页内容缩小整个iframe
  3. 以下是一个例子:

    <div id="wrap">
       <IFRAME ID="frame" name="Main" src ="http://www.google.com" />
    </div>
    

    <style type="text/css">
        #wrap { width: 130px; height: 130px; padding: 0; overflow: hidden; }
        #frame { width: 900px; height: 600px; border: 1px solid black; }
        #frame { zoom:0.15; -moz-transform:scale(0.15);-moz-transform-origin: 0 0; }
    </style>
    

答案 19 :(得分:0)

iGoogle小工具必须积极实施调整大小,因此我的猜测是在跨域模型中,如果没有远程内容以某种方式参与,则无法执行此操作。如果您的内容可以使用典型的跨域通信技术将具有新大小的消息发送到容器页面,那么其余的很简单。

答案 20 :(得分:-2)

我认为应该有所作为。

parent.document.getElementById(iFrameID).style.height=framedPage.scrollHeight;

将您的正文加载到iframe内容上。

答案 21 :(得分:-2)

这有点棘手,因为您必须知道iframe页面何时加载,这在您无法控制其内容时会非常困难。它可以为iframe添加一个onload处理程序,但我在过去尝试过它,它在浏览器中的行为有很大不同(不要猜猜谁最烦人......)。您可能必须向执行调整大小的iframe页面添加一个函数,并将一些脚本注入到侦听加载事件或调整事件大小的内容中,然后调用前一个函数。我想在页面上添加一个函数,因为你想确保它的安全,但我不知道它会有多容易。

答案 22 :(得分:-4)

我有一个简单的解决方案,需要您确定链接的宽度和高度,请尝试(适用于大多数浏览器):

<a href='#' onClick=" document.getElementById('myform').src='t2.htm';document.getElementById('myform').width='500px'; document.getElementById('myform').height='400px'; return false">500x400</a>