三谈Iframe自适应高度[转自口碑网UED博客]

说明:本篇文章转自口碑网UED博客,原文地址:http: // ued.koubei.com/?p=1217  转载请作必要的版权说明,谢谢!

在构建B/S系统界面的时候,经常会遇到主页面index.html中嵌套其他页面的情况 ,虽然已经有的库已经提供了控件(例如jQuery easy UI),但是有时候iframe的使用是不可避免的,这篇文章应该给你一个相对来说比较实用的答案,正象文章所说,互联网上的大部分东西都是垃圾或者是不可相信的!原文有KOUBEI UED  由校 整理!

 

为什么是三谈

为什么是三谈呢?一是因为这真的是一个被说烂的话题,二是因为太师傅在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 );


 


 

 


 



 

 

 

 

 

 


 

你可能感兴趣的:(iframe)