不使用定时器实现iframe的自适应高度

时间:2022-06-14
本文章向大家介绍不使用定时器实现iframe的自适应高度,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在微博上看到有人提及不使用定时器实现iframe自适应(onReadyStateChange + onLoad + onResize + onDOMSubtreeModified),然后就去折腾了,这篇与之前的文章:《不使用定时器实现onhashchange》有点类似

/*****此方法暂时只支持同域下,跨域的问题有待解决****/

以往要使iframe的高度自适应,往往用定时器在跑,这个方法不错。但如果遇到这样的场景,可能会有点问题,就是某个页面嵌入一个app页面(iframe),

而这个app页面,可能经常会发生一些dom的更改,而且是由成千上万的第三方开发者开发的。而且如果定时器一直开着(只要iframe存在),总归不太好~

这样就面临着一个问题:

开发者可能需要对DOM进行修改,而iframe的高度如果需要改变,就必须由第三方开发者调用父层的,每一处DOM修改都要调用一次…

把调整iframe高度的方法暴露给第三方开发者,显示不大合适。有没有更好的方法,有,那就是DOMSubtreeModified。

在折腾的过程中,其实遇到了很我问题,不过基本上通过google就可以解决掉。某人讲的话还是挺有道理的:“Web前端开发无难点,贵在研究问题的精神和过程,方法论只是结果,价值观才是精髓~”

这个属于DOM 3 Level的事件,关于此事件的详情,可以参考以下网址:

MDC DOMSubtreeModified >>

W3C DOMSubtreeModified>>

相应的还有DOMAttrModified、DOMNodeInserted、DOMNodeRemoved等等事件

举个DOMSubtreeModified的简单例子:

1: /**

       2:  * ( modified from Nicholas's book )

       3:  */

       4: (function() {

       5:     var _EventUtil = {

       6:         /**

       7:          * 注册event handler

       8:          */

       9:         addHandler: function( element, type, handler ) {

      10:             if( element.addEventListener ) {

      11:                 element.addEventListener( type, handler, false );

      12:             } else if( element.attachEvent ) {

      13:                 element.attachEvent( 'on' + type, handler );

      14:             } else {

      15:                 element[ 'on' + type ] = handler;

      16:             }

      17:         },

      18:         /**

      19:          * 停止event capturing and bubbling

      20:          */

      21:         preventDefault: function( event ) {

      22:             if( event.preventDefault ) {

      23:                 event.preventDefault();

      24:             } else {

      25:                 event.returnValue = false;

      26:             }

      27:         },

      28:         /**

      29:          * 获取事件触发的事件源

      30:          */

      31:         target: function( event ) {

      32:             return event.target || event.srcElement;

      33:         },

      34:         /**

      35:          * 获取event对象

      36:          */

      37:         event: function( event ) {

      38:             return event || window.event;

      39:         }

      40:     }

      41:     window.EventUtil = _EventUtil;

      42: })();

页面内容

1: <a href="#" id="show">Show</a>

       2: <div style="display: none;" id="sqr1">

       3: </div>

绑定的事件:

1: var attrChangeListener = function( elem, fn ) {

       2:     EventUtil.addHandler( elem, 'DOMSubtreeModified', fn );

       3: };

       4:  

       5: EventUtil.addHandler( window, 'load', function() {

       6:     var showElem = document.getElementById( 'show' );

       7:     var sqr1 = document.getElementById( 'sqr1' );

       8:     

       9:     EventUtil.addHandler( showElem, 'click', function() {

      10:         if( sqr1.style.setAttribute ) {

      11:             sqr1.style.setAttribute( 'display', 'block' );

      12:         } else {

      13:             sqr1.setAttribute( 'style', 'display:block;' );

      14:         }

      15:     } )

      16:     

      17:     attrChangeListener( sqr1, function( event ) {

      18:         alert( EventUtil.target( event ) );

      19:         alert( event.type );

      20:         alert( 'Attrubute Name:' + ( event.attrName || '' ) );

      21:         alert( 'Attribute Change:' + ( event.attrChange || '' ) );

      22:         alert( 'Previous value:' + ( event.prevValue || '' ) );

      23:         alert( 'New value:' + ( event.newValue || '' ) );

      24:     } );

      25: } );

      26:

在线预览地址>>  请使用Firefox进行查看

解决iframe自适应高度的问题,比较理想的办法是:

iframe的onload前使用定时器修改iframe的高度,在onload后清除定时器,然后监听iframe它的document的DOMSubtreeModified事件。为什么在onload之前还要使用定时器呢?防止iframe页面加载资源过久,页面的高度显示上会有问题。而监听DOMSubtreeModified事件的主要作用是为了省去在iframe内修改dom时,每一次都要主动调用一次修改iframe高度的方法。这样就让iframe开发者,只需要专注自身页面的逻辑结构,不用再考虑每修改dom之处都要调用修改iframe高度的方法。

注明:文章的标题是不使用定时器,而上面我提到定时器,主要是担心iframe的domready与onload的那段时间内,iframe的高度看上去会很怪异(实际开发中这一段时间有多长,影响有多大,到底要不要加定时器,还是需要根据实际情况再衡量一下)

下面的实现,我没有考虑使用定时器(如果加上了就不符合文章的标题了,而在实际开发中可能还是需要,视情况而定了),关于使用定时器使iframe自适应高度,可以参考口碑的那篇文章:再谈iframe自适应高度>>

还有一点要提一下:chrome的某些版本中,子页(iframe)调用parent时会被禁止,而导致页面没有效果,放在web上跑就好了。这个问题可以参考这里找到说明,地址>>

下面直接上代码了,代码中有参考老外的例子,在整理过程中没有及时的保存这些链接,有空再补上。

例子是index.html嵌入iframe.html页面,index.html代码:

1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

       2: <html xmlns="http://www.w3.org/1999/xhtml">

       3:  <head>

       4:   <title>iframe 高度自适应的例子</title>

       5:   <meta name="generator" content="editplus" />

       6:   <meta name="author" content="" />

       7:   <meta name="keywords" content="" />

       8:   <meta name="description" content="" />

       9: <meta http-equiv="content-type" content="text/html;charset=utf-8" />

      10:   <style type="text/css">

      11:     *  {margin:0; padding:0;}

      12:     body {background-color:#fff; padding:20px;}

      13:     iframe {border:1px solid #406c99; width:600px;}

      14:     button {position:absolute; left:20px; top:20px; width:80px;}

      15:   </style>

      16:  </head>

      17:  

      18:  <body>

      19:   <button onclick="createFrame()">创建iframe</button>

      20:  </body>

      21: </html>

      22: <script type="text/javascript">

      23: function createFrame() {

      24:     document.getElementsByTagName("button")[0].disabled = true;

      25:  

      26:     frameHandler.create();

      27: }

      28:  

      29: var frameHandler = function() {

      30:     var inner;

      31:     

      32:     var _iframeName = "iframe_iden_1";

      33:  

      34:     return inner = {

      35:         _isSupport : false,

      36:         init : function() {

      37:             

      38:         },

      39:         create : function() {

      40:             var frame = null;

      41:  

      42:             if (window.ActiveXObject) {

      43:                 frame = document.createElement('<iframe name="'+_iframeName+'">');

      44:             } else {

      45:                 frame = document.createElement("iframe");

      46:                 frame.setAttribute("name", _iframeName);

      47:             }

      48:             

      49:             frame.setAttribute("id", _iframeName);

      50:             frame.frameBorder = "none";

      51:             frame.scrolling = "no";

      52:             frame.style.marginTop = '40px';

      53:             

      54:             document.body.appendChild(frame);

      55:  

      56:             frame.contentWindow.focus();

      57:  

      58:             inner.check();

      59:             

      60:             if (inner._isSupport) {

      61:                 if (!frame.addEventListener) {

      62:                     frame.attachEvent("onload", function() {

      63:                         frame.detachEvent("onload", arguments.callee);

      64:                         inner.adjustFrameHeight();

      65:                         frame.contentWindow.attachEvent("onresize", inner.adjustFrameHeight);                        

      66:                     });

      67:                     

      68:                 } else {

      69:                     frame.addEventListener("load", function() {

      70:                         frame.removeEventListener('load', arguments.callee, false);

      71:                         inner.adjustFrameHeight();

      72:                         frame.contentWindow.document.documentElement.addEventListener('DOMSubtreeModified', inner.adjustFrameHeight, false);

      73:                     }, false);

      74:                 }

      75:             } else if (frame.addEventListener) {// for FF 2, Safari 2, Opera 9.6+

      76:                     frame.addEventListener("load", function() {

      77:                         var fn = arguments.callee;

      78:                         setTimeout(function() {

      79:                             frame.removeEventListener('load', fn, false);

      80:                         }, 100);

      81:                         

      82:                         inner.adjustFrameHeight();    

      83:                         frame.contentWindow.document.documentElement.addEventListener('DOMNodeInserted', inner.adjustFrameHeight, false);

      84:                         frame.contentWindow.document.documentElement.addEventListener('DOMNodeRemoved', inner.adjustFrameHeight, false);

      85:                     }, false);

      86:             }

      87:  

      88:             frame.src = "iframe.html";

      89:         },

      90:         getFrame : function() {

      91:             return document.getElementById(_iframeName).contentWindow;

      92:         },

      93:         adjustFrameHeight : function() {

      94:             var elem = document.getElementById(_iframeName);

      95:  

      96:             elem.style.height = Math.max(elem.contentWindow.document.body.scrollHeight, elem.contentWindow.document.documentElement.scrollHeight) + 'px';

      97:         },

      98:         check : function() {

      99:             var remain = 1, 

     100:                 doc = document.documentElement, 

     101:                 dummy;

     102:             

     103:             if (doc.addEventListener) {

     104:                 doc.addEventListener("DOMSubtreeModified", function() {

     105:                     inner._isSupport = true;

     106:                     doc.removeEventListener("DOMSubtreeModified", arguments.callee, false);

     107:                 }, false);

     108:             } else {

     109:                 inner._isSupport = true;

     110:                 return ;

     111:             }

     112:  

     113:             dummy = document.createElement("div");

     114:             doc.appendChild( dummy );

     115:             doc.removeChild( dummy );

     116:         }

     117:     }

     118: }();

iframe.html的代码:

1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

       2: <html xmlns="http://www.w3.org/1999/xhtml">

       3:  <head>

       4:   <title> new document </title>

       5:   <meta name="generator" content="editplus" />

       6:   <meta name="author" content="" />

       7:   <meta name="keywords" content="" />

       8:   <meta name="description" content="" />

       9:   <meta http-equiv="content-type" content="text/html;charset=utf-8" />

      10:   <style type="text/css">

      11:     * {margin:0; padding:0;}

      12:     body { padding:5px; background-color:#fff;}

      13:     button {margin:0 10px;}

      14:   </style>

      15:  </head>

      16:  

      17:  <body>

      18:   xxxxxxxxx

      19:  

      20: <button disabled="true" onclick="start()">Start Interval</button>

      21: <button onclick="stop()">Stop Interval</button>

      22: <button onclick="clearAllChilds()">Clear All</button>

      23:  

      24:  </body>

      25: </html>

      26: <script type="text/javascript">

      27: var g_interval_timer = null;

      28:  

      29: function start() {

      30:     commonHandler(0);

      31:  

      32:     g_interval_timer = setInterval(function(){    

      33:         var div = document.createElement("div");

      34:             div.innerHTML = "<p>dddddddddddddddddddddddddddd</p>";

      35:         document.body.appendChild(div);

      36:     }, 100);    

      37: }

      38:  

      39: function stop() {

      40:     commonHandler(1);    

      41: }

      42:  

      43: function clearAllChilds() {

      44:     stop();

      45:     var childs = document.getElementsByTagName("div");

      46:     

      47:     while (childs.length) {

      48:         document.body.removeChild(childs[0]);

      49:     }

      50: }

      51:  

      52: function commonHandler() {

      53:     var buttons = document.getElementsByTagName("button"),

      54:         idx = arguments[0];

      55:  

      56:     if (g_interval_timer) {

      57:         clearInterval(g_interval_timer);

      58:         g_interval_timer = null;

      59:     }

      60:     

      61:     buttons[idx].disabled = true;

      62:     buttons[1 - idx].disabled = false;

      63: }

      64:  

      65: start();

下面本地用nginx建了一个域名ajax.com运行的效果:

IE6(7、8效果与此类似,但不知为何一定要让iframe foucs后才有效果,详见代码)

Chrome:

FF:

Safari:

Opera:

完整例子的下载地址>>