网络编程
位置:首页>> 网络编程>> JavaScript>> 三谈Iframe自适应高度

三谈Iframe自适应高度

作者:由校 来源:koubei ued 发布时间:2010-08-03 13:04:00 

标签:iframe,自适应高度,javascript

为什么是三谈


为什么是三谈呢?一是因为这真的是一个被说烂的话题,二是因为太师傅在n年前就写过这篇再谈iframe自适应高度。之所以再提该问题,是因为之前项目中确实遇到了这个问题的方方面面,有必要总结一下。希望对各位有帮助,有错误请指正。

同域、子页面高度不会动态增加


这种情况最简单,直接通过脚本获取字页面实际高度,修改iframe元素高度即可。但有二点必须注意:

  1. 如果页面内有绝对定位或者没有清浮动的元素,情况有些复杂,不同浏览器处理结果不同,甚至包括Webkit内核的浏览器,具体请看这个Demo。所以你要么进行浏览器检测,要么用Math.max计算一个最大值,要么你想别的方法。

  2. iframe所包含页面可能非常大,需要很长的加载时间,为此直接计算高度的时候,很可能页面还没下载完,高度计算就会有问题。所以最好在iframeonload事件中计算高度。这里还要注意的是,IE下必须使用微软事件模型obj.attachEvent来绑定onload事件。而别的浏览器直接obj.onload = function(){}也可以。


(function(){    var frame = document.getElementById("frame_content_parent"),        setIframeHeight = function(){            var frameContent = frame.contentWindow.document,                frameHeight = Math.max(frameContent.body.scrollHeight,frameContent.documentElement.scrollHeight);            frame.height = frameHeight;        };    if(frame.addEventListener){        frame.addEventListener("load",setIframeHeight,false);    }else{        frame.attachEvent("onload",setIframeHeight);    }})();


同域、子页面高度会动态增加


原理与第一种情况一样,多一个计时器,一直检测字页面高度,当子页面高度和iframe的高度不一致时,重新设置iframe的高度。这边也可以加一个try在js出错时,加一个足够的高度。



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


同域、子页面高度会动态增加、脚本可能完全失效


第二个例子中,考虑到了脚本出错的情况,但是万一脚本根本不执行了呢,那iframe中的内容就会因为iframe的高度不够而显示不了。为此我们通常事先设置一个足够的高度,为了前端控制方便,我觉得写在CSS文件中比较合适,需要修改时只改CSS就行了。这里我设置了selector{ height:1800px; }。需要注意的是,写在样式表里的样式,不能直接用node.style[property]来取,对于微软模型,要用node.currentStyle[property](题外话:悲剧的IE模型不支持CSS伪类),对于W3C模型,要用window.getComputedStyle(node,null)[property]来取。我这里图方便直接用了YUI。

这里又有一个问题,设置iframe的高度大于其包含页面的高度时,各个浏览器的处理不一样。例如在Firefox下,必须计算body元素的高度,而html元素的高度等于iframe的高度,然而当恰巧这个页面又有绝对定位未清浮动元素时,又不能通过body元素来取,显然第一种方法缺点更小一些。具体请看这个Demo

从上面这个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的高度。基本的原理就是这样,看代码吧:

DEMO



//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页面增加计时器的原因。

DEMO



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


 

0
投稿

猜你喜欢

  • 发现这个也是偶然,在测试的时候发现的,因此问题还发现一个bug。蛮有意思~ 假如输入http://www.aspxhome.com的话,在
  • 以前在工作中遇到一个问题,当表单发送的数据量很大时,就会报错。查阅MSDN了解到,原因是微软对用Request.Form()可接收的最大数据
  • 前几天,酋长同学在日志里提到了关于Google宽松的管理制度,一个产品任务下来是没有时间限制的,Google深信在有时间限制下的产品肯定是不
  • 如何修改NT的登录密码?    代码见下:<%Sub ChangeUserPassword(C
  • 两个文章,放在一块了。第二个比较强。====================================================
  • 问题:SQL Server 2005中如何利用xml拆分字符串序列?解答:下文中介绍的方法比替换为select union all方法更为见
  • 在我前一阵子刚刚写了“HTML5与Flash,不得不说的话题”的评论后,如各位所料,由于牵扯到多方利益和未来标准制定的角色份量,这不,有可能
  • 使用SQL SERVER的[导入]功能,便可将access数据转换,但要注意原来的'自增字段'需要修改,将相应字段标识修改为
  • 一、问题描述 SQL Plus WorkSheet是一个窗口图形界面的SQL语句编辑器,对于那些喜欢窗口界面而不喜欢字符界面的用户,该工具相
  • 是的,我在这里要说:注册表单将死(好拗口啊,但是不知道怎么翻译更恰当)。回想一下,当你想享受一个网站服务的时候,碰到的第一件事是什么?一个表
  • 在MySQL数据库中导出整个数据库:1.导出整个数据库mysqldump -u 用户名 -p 数据库名 > 导出的文件名mysqldu
  • acronym从字义上理解,是取首字母的缩写词,abbr是缩写,在应用过程中,两个标签看起来差不多,但个人觉得还是有区别的。看下面的例子:&
  • 来看看效果图对比:字符验证码: → 加法验证码:优点:①与纯字符验证码相比,本程序效防止了绝大部分(99%以上)广告机的自动识别。即使是中文
  • asp防止用户同时登陆的方法,实现这个功能可有两种方式:1.使用application用application对象:如果做的是大型社区,可能
  • 在异步应用程序中发送和接收信息时,可以选择以纯文本和 XML 作为数据格式。掌握 Ajax 的这一期讨论另一种有用的数据格式 JavaScr
  • 如果想单独取background-position的X值或Y值,IE里可以通过私有属性background-positionX或者backg
  • MySQL中有许多操作符和函数可以返回字符串。本节回答这个问题:返回的字符串使用什么字符集和 校对规则?对于简单的函数,即接收字符串输入然后
  • 本文探讨了提高MySQL数据库性能的思路,并从8个方面给出了具体的解决方法。1、选取最适用的字段属性MySQL可以很好的支持大数据量的存取,
  • 最近在无忧脚本混了一阵子,回复了一些贴子,自己却没有做出什么东东让大家看看,心里有些不安,于是写了下边的一点东西,本来应该发在类封装区的,考
  •  网页离不开链接,而默认链接的表现形式总是千篇一律的蓝色文字加底线,每当我们点击它时,周围会出现虚线框,表示该链接是当前的焦点,影
手机版 网络编程 asp之家 www.aspxhome.com