• iframe的onload事件


    很多时候,我们会需要改变一个iframe的地址(src属性),或者使用表单(form)的target在指定的iframe进行提交后,在 iframe加载完毕(onload)时立即响应某个操作,以提高WEB应用程序的价值。本文讨论了跨浏览器的iframe onload事件的监听方法。

    如果你没时间去阅读全文,可以看解决方案的内容概要:

    1. 同域的页面嵌套,最好的是让内嵌的页面调用父页面的函数,如 window.parent.callparentFunctoin()。
    2. 如果是异域,或者子页面已存在且无法修改,那么:在Firefox/Opera/Safari中,可以直接使用iframe onload事件;而在IE中,可以通过定时器测定子页面的document.readyState,或者使用iframe onreadystatechange事件计算该事件的响应次数。

    以上内容基于参考文档: Q239638 和 Q188763.

    如果你对这个话题很感兴趣,请一定要继续阅读哦。。。

    注[1]:为了使问题更集中,本文所述的<iframe>均直接写在父页面中,对使用 document.createElement(“iframe”)或者其它方式建立的iframe不作讨论,因为这样会使问题在IE下变得更加复杂,但只要使用本文的结论,无论何方式下建立的iframe,问题仍然会得到解决。

    一个简单的包含iframe的父页面将是如下样子的:

    <!DOCTYPE ...> <html xmlns="...">   <head>   <meta ... />   <title>Iframe</title>   <body>     <iframe name="iframe1"  id="iframe1" width="300" height="50" src="#" ></iframe>     <script type="text/javascript">//codes here</script>   </body> </html>

    开始:

    让我们从一种简单的情形和解决方法开始:

    1.WINDOW.PARENT 对象

    1.1 调用父页面对象

    <!–This is an inner page in the iframe–>
    <script type=”text/javascript”>
    window.onload=function{ window.parent.iframeCall();}
    </script>

    在网上找到的方法中,最令人开心的一个,莫过于在能子页面中调用父页面的对象了:

    window.parent.callFunciton()。

    不过我想:可能这点全地球人都已经知道了。只是这个方法有一个缺点,那就是子父页面必须在同域中。

    还有一点,就是前端工程师们需对子页面有修改权;或者,可以请负责此子页面的同事为我们添加一段代码:

    <script type=”text/javascript”>
    if(window.parent!=window) window.parent.iframeCall();
    </script>

    把它放到window.onlad中,或者直接放在</body>之前。

    注[2]:在对iframe或其它窗口性质的前端编程中,同域名是最完美的先天条件。只要在同一域名中,各个窗口间的对象是共享的,我们完全可以自由发挥,在不同的窗口间来回驾驭。总之,只有想不到,没有做不到。

    1.2 异域

    在不同域名的页面,浏览器出于安全考虑,几乎完全封锁了页面间的对象来往,这里没有鹊桥,牛郎和织女只能远远想望。当然,用iframe嵌套页面还是可以的,毕竟还可以思念。

    面对家族的封锁,罗密欧还是很想见朱丽叶,他在夜里架起梯子抓到朱丽叶的窗前与她见面;在异域的页面嵌套中,子页面总是可以直接改变父窗口的location以防止被嵌套,但父页面对这个一点办法也没有。

    当然,子页面除了仅仅永恒地拥有父窗口.location的修改权外,也没有其它了。例如,在IE下,子页面只能直接修改父页面的.location为另一个源:

    <script tyle=”text/javascript”>parent.location=”http://anotherPage.com/”;</script>

    但无法访问其它对象,如window.name,document等,连location.href等location的子属性就无法访问。当然,在防止嵌套方面,使用top.location会更强大。

    但Firefox中,似乎还可以为top.location添加一些东西,但这是在我不严谨的测试中出现过的情况,未经找到相应的权威文档哦。

    2. IFRAME ONLOAD 事件

    在Firefox/Opera/Safari中,直接使用frame元素的onload事件即可:
    document.getElementById(“iframe1”).onload=function(){
    //your codes here.
    };
    只可惜它在IE下经常无效,因为在IE下它最多只能被激活一次,而且无论你有多少个iframe,被激活的也只能是最后一个的。更详细的描述请看:Q239638 和 Q188763

    原因
    这些事件是在IFRAME内的文档对象模型中激活的,而不是父页面的。在IFRAME加载完毕的时候,这个事件就被激活了,而且ReadyState已经是“完成”状态。所以你无法通过这个事件来查检一个IFRAME是否加载完毕。

    为了得到更好的表现,我们再稍稍研究一个问题:IFRAME递归。

    3.IFRAME 递归

    在处理IFRAME时,浏览器应该有一个基本规则,那就是防止递归,防止页面无限的自我加载,使客户端设备崩溃。事实上,文中出现的几个浏览器均做到这点,只是不同的浏览器有不同的处理方式。请分别尝试以下代码:
    <iframe src=”” onload=”finish()” name=”iframe1”></iframe>
    <iframe src=”#hashonly” onload=”finish()” name=”iframe2”></iframe>
    <iframe src=”?search” onload=”finish()” name=”iframe3”></iframe>
    <iframe src=”http://anotherPage.com” onload=”finish()” name=”iframe4”></iframe>
    执行的结果是,在父页面加载时,上面的iframe onload函数在IE/Opera/Safari中均会被激活,Firefox对第二个没有反应。这主要因为他们在防止递归方面的处理是不同的。
    对于#hashonly和?search这样的URL,浏览器会解释为页面本身。但hash和search的不同之处是,改变 search可以组成新的源,而改变hash不会。通常地,浏览器一遇到同源的iframe内页即会停止加载,但Safari却会加载多一次。
    假如把finish()函数写成如下:
    var finsh=function(){alert(”onload from :”+this.src);}
    运行时分别弹出的消息弹出框的次数如下:

    ifm/brw:    IE    |    Firefox    |    Opera    |    Safari iframe1:    1     |       1       |      1      |      0 iframe2:    1     |       0       |      1      |      1 iframe3:    2     |       1       |      2      |      2 iframe4:    1     |       1       |      1      |      1

    再结合页面所呈现的内容,可得看出这些浏览器在处理递归问题上的一些细则:

    • Firefox 不会在iframe中加载任何东西和激活onload事件(可能是任何事件)
    • IE和Opera不会在iframe中加载页面,但会激活onload事件。
    • Safari(windows版本)会在iframe中加载页面一次且仅仅一次,并会激活onlaod事件且仅激活依附在父页面上那个iframe的onload事件。

    关于本节,如果仅把iframe用于页面嵌套,那意义不大;如果用于动态加载/呈现内页,或者用于良好用户体验的form target表单提交处理(不是Ajax),并且要求较高的浏览器兼容性时,作用才会显示出来。根据本节结果,为了提高兼容性,最好事先把iframe指向一个空页面——blank.html,因为它在4种浏览器中的表现是一样的。如果不想事先加载页面,那就得花多点心思去判断浏览器类型了。

    4.代码实现

    4.1Firefox/Opera/Safari,直接使用iframe onload事件

    document.getElementById(“iframe1”).onload=function(){     //your codes here. };

    4.2在IE下,定时器测document.readyState或者注册iframeonreadystatechange事件

    4.2.1定时器以及document.readyState

    var fm1=window.frames["iframe1"]; var fmState=function(){   var state=null;   if(document.readyState){     try{       state=fm1.document.readyState;     }catch(e){state=null;}     if(state=="complete" || !state){//loading,interactive,complete       //onComplete();       return;     }     window.setTimeout(fmState,10);   } }; //在改变src或者通过form target提交表单时,执行语句: if(fmState.TimeoutInt) window.clearTimeout(fmState.timeoutInt); fmState.timeoutInt = window.setTimeout(fmState,400);

    为什么要延时400毫秒?因为javascript对DOM的操作是异步的,我们必须等待脚本对DOM落实执行后才开始下一步。400秒这个数取决与客户端的设备和浏览器的响应速度,好的设备的响应速度能在10毫秒以内甚至更快,但100毫秒左右可能比较大众化,400毫秒应该是十分保守的了。总之,较大的毫秒数能适合更多的用户设备状况,并能减少了客户端设备的工作量。

    至于document.readyState,指的是iframe内子页的docuent.readyState,而不是父页面的。只要允许,即在同域情况下,document.readyState会返回5个状态:

    uninitialized对象未初始化.
    loading对象正在加载数据.
    loaded对象已加载完数据.
    interactive在这个状态下,用户可以参与互动,即使在对象未加载完毕也可以
    complete对象已完成初始化.

    为什么使用try和 catch?因为在异域的情况下,当iframe的子页到达interactive状态时,父页面就会失去访问权,所以最多只能返回到loaded这一步,因此IE出一个未知错误——其实就是没有权限,所以try和catch,让这个错误沉默下去。

    幸好这个方法只针对IE(目前我能使用到的版本:IE6/7),否则麻烦大了:Opera不等页面加载完就开始交互了,而IE会等页面加载完毕才进行交互,所以感觉用Opera打开网页的速度相对比IE快。

    4.2.2.onreadystatechange 事件三步曲

    var stateID={}; var fmStChange=function(){   if(ifFirstLoad) return;   stateID[this.id]=stateID[this.id] ? stateID[this.id]+1:1;   switch (stateID[this.id]){     case 1:       //state loading       //onComplete(STEP1);       break;     case 2:       //state interactive       //onComplete(STEP2);       break;     case 3:       //state complete       //onComplete(LASTSTEP);       break;   }   if(stateID[this.id]&gt;=3) stateID[this.id]=null; }; $("iframe1").onreadystatechange=fmStChange; //if you want to ignore the parent page load //add the following two line var ifFirstLoad=true; $("iframe1").onload=function(){ifFirstLoad=false;}

    每当iframe加载页面,过程内会激活onreadystatechange事件三次,相应的状态分别是loading,interactive和complete,而最后一次才是complete,所以我们得计算一下,直到第三次才算是完成。

    注意:这个方案中的stateID在状态判断时非常重要,要进行必要的修正和保护,如在每次应用iframe 时,把stateID[iframe_id]复位为null,或者在第三次响应完成之前,不要对iframe进行新轮页面加载,或者在新一轮的页面加载前消除之前的事件并复位。

  • 相关阅读:
    leetcode 155. Min Stack 、232. Implement Queue using Stacks 、225. Implement Stack using Queues
    leetcode 557. Reverse Words in a String III 、151. Reverse Words in a String
    leetcode 153. Find Minimum in Rotated Sorted Array 、154. Find Minimum in Rotated Sorted Array II 、33. Search in Rotated Sorted Array 、81. Search in Rotated Sorted Array II 、704. Binary Search
    leetcode 344. Reverse String 、541. Reverse String II 、796. Rotate String
    leetcode 162. Find Peak Element
    leetcode 88. Merge Sorted Array
    leetcode 74. Search a 2D Matrix 、240. Search a 2D Matrix II
    Android的API版本和名称对应关系
    spring 定时任务执行两次解决办法
    解析字符串为泛型的方法
  • 原文地址:https://www.cnblogs.com/top5/p/2292933.html
Copyright © 2020-2023  润新知