为什么是三谈
为什么是三谈呢?一是因为这真的是一个被说烂的话题,二是因为太师傅在n年前就写过这篇再谈iframe自适应高度。之所以再提该问题,是因为之前项目中确实遇到了这个问题的方方面面,有必要总结一下。希望对各位有帮助,有错误请指正。
同域、子页面高度不会动态增加
这种情况最简单,直接通过脚本获取字页面实际高度,修改iframe元素高度即可。但有二点必须注意:
如果页面内有绝对定位或者没有清浮动的元素,情况有些复杂,不同浏览器处理结果不同,甚至包括Webkit内核的浏览器,具体请看这个Demo。所以你要么进行浏览器检测,要么用Math.max计算一个最大值,要么你想别的方法。
iframe所包含页面可能非常大,需要很长的加载时间,为此直接计算高度的时候,很可能页面还没下载完,高度计算就会有问题。所以最好在iframe的onload事件中计算高度。这里还要注意的是,IE下必须使用微软事件模型obj.attachEvent来绑定onload事件。而别的浏览器直接obj.onload = function(){}也可以。
(function(){ var _reSetIframe = function(){ var frame = document.getElementById("frame_content_parent") try { var frameContent = frame.contentWindow.document, bodyHeight = Math.max (frameContent.body.scrollHeight, frameContent.documentElement.scrollHeight); if (bodyHeight != frame.height){ frame.height = bodyHeight; } } catch(ex) { frame.height = 1800; } } if(frame.addEventListener){ frame.addEventListener ("load",function(){setInterval(_reSetIframe,200);},false); }else{ frame.attachEvent("onload",function(){setInterval(_reSetIframe,200);}); } })(); |
从上面这个Demo可以看到,除IE浏览器外,别的浏览器计算出来的都是iframe的高度,即CSS里设置的#frame_content_parent{ height:1800px; }。而IE计算出来的是iframe所引用页面的实际高度。
#frame_content_parent{ height:1800px; } (function(){ var $ = YAHOO.util.Dom, frame = $.get("frame_content_parent"); function reSetIframe(){ var frameContent = frame.contentWindow.document, bodyHeight = Math.max (frameContent.documentElement.scrollHeight, frameContent.body.scrollHeight); if (bodyHeight != $.getStyle(frame, "height")){ $.setStyle(frame, "height", bodyHeight + "px"); } } if(frame){ $.setStyle(frame,"height","auto"); setInterval(reSetIframe,300); } })(); |
跨域
这里提供一个Iframe代理的方法,简单地说一下原理。假设有3个页面,分别是主页面A.html,字页面B.html,代理页面C.html。其中A与B是跨域的,而A和C是同域的。它们的关系:A包含B,B包含C。很显然A和B,以及B和C,因为跨域不能相互通信,而A和C同域,可以相互通信。为此我们就想到让C页面告诉A页面,B页面到底有多少高。因为B和C也是跨域的不能相互通信,所以想在C页面中,直接window.parent.document.body.scrollHeight这样是行不通的,所以我们只能让B页面自己计算自身的高度,然后通过某种方法告诉C页面,再由C页面告诉A页面。这里的一个方法就是在B页面生成一个Iframe节点,然后设置它的src属性,在这个地址上附加一个参数,即B页面计算出来的高度,然后C页面就可以通过window.location获取这个地址栏中的地址,提取出高度值,通过window.top找到A页面,设置A页面的Iframe的高度。基本的原理就是这样,看代码吧:
//B页面脚本 //任务:计算其实际高度,然后生成一个iframe节点,将高度作为代理页面C的地址的一部分赋值给Src属性 (function(){ var agent_iframe = document.createElement("iframe"), b_height = Math.max(document.documentElement.scrollHeight,document.body.scrollHeight); agent_iframe.src = "http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe_once.html#" + b_height; document.body.appendChild(agent_iframe); agent_iframe.style.display = "none"; })(); |
//C页面脚本 //任务:获取请求地址中的高度值,将其赋值给A页面的Iframe的高度 window.top.document.getElementById("frame_content_parent").height = parseInt(window.location.hash.substring(1),10); |
跨域、字页面高度动态变化
这里结合了第2、第4两种方法,我的想法是在B页面通过一个计时器,不停计算B页面的高度,一但变化,马上修改iframe标签的src属性,而C页面也有计时器不断监听src的变化,改变Aiframe标签的高度。需要注意的是仅仅修改src属性后面的锚点值(如“#1234”),页面并不会刷新,不会重新请求,这也是在C页面增加计时器的原因。
//B页面脚本 (function(){ var getHeight = function(){ return Math.max(document.documentElement.scrollHeight,document.body.scrollHeight); }; var preHeight = getHeight(), agent_iframe; var createIframe = function(height){ agent_iframe = document.createElement("iframe"); agent_iframe.style.height = "0"; agent_iframe.style.width = "0"; agent_iframe.style.border = "none"; agent_iframe.src = "http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe.html#" + height; document.body.appendChild(agent_iframe); } createIframe(preHeight); var checkHeight = function(){ var currentHeight = getHeight(); if(currentHeight != preHeight){ agent_iframe.src = "http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe.html#" + currentHeight; preHeight = currentHeight; } setTimeout(checkHeight,500); } setTimeout(checkHeight,500); })(); |
//C页面脚本 (function(){ var preHeight = parseInt(window.location.hash.substring(1),10), ifrmae = window.top.document.getElementById("frame_content_parent"); ifrmae.height = preHeight; setInterval(function(){ var newHeight = parseInt(window.location.hash.substring(1),10); if (newHeight !== preHeight){ ifrmae.height = newHeight; preHeight = newHeight; } },500); })(); |
这里还有另一种方案,就是让iframe每一次都重新请求,这样C页面就不需要计时器了,但是如果2次计算高度重复的话,就会导致src属性的值相同,这样浏览器就很可能不重新请求该页面了,那么C页面中的脚本也就不运行了。要修复这个问题很简单,只要在每次计算出来的src属性上增加一个随机数的参数就行了。比如http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe.html?temp=123123423712937#1563
//B页面关键脚本 agent_iframe.src = "http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe.html?a=" + Math.random() + "#" + currentHeight; |
//C页面脚本 window.top.document.getElementById("frame_content_parent").height = parseInt(window.location.hash.substring(1),10); |