`

跨浏览器的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.1.Firefox/Opera/Safari,直接使用iframe onload事件

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

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

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进行新轮页面加载,或者在新一轮的页面加载前消除之前的事件并复位。
分享到:
评论

相关推荐

    iframe高度自适应,多浏览器兼容

    2. **IE8**:虽然支持`contentWindow`和`contentDocument`,但没有`MutationObserver`,所以需要在`window.onload`或`iframe.onload`事件中处理高度自适应。 3. **Firefox**:Firefox通常能较好地处理`iframe`,但...

    iframe自适应高度(兼容多种浏览器)

    2. **事件监听**:除了在页面加载完成后调用调整高度的方法外,还应该监听窗口的`resize`事件,以便在窗口大小改变时重新计算`iframe`的高度。 3. **错误处理**:在某些情况下,如`iframe`加载失败或内容为空时,...

    解决IE,Firefox,chrome,safari浏览器中iframe显示高度自适应问题

    4. **修改`iframe`标签**:在`iframe`标签中添加`onreadystatechange`和`onload`事件监听器,分别在IE和其他浏览器中调用相应的函数。 #### 示例代码 ```html function stateChangeIE(_frame) { if (_frame....

    JS实现兼容火狐及IE iframe onload属性的遮罩层隐藏及显示效果

    然而,由于历史原因,不同浏览器(比如Firefox和Internet Explorer)对iframe的onload事件的实现存在差异,导致直接编写跨浏览器代码可能会遇到兼容性问题。 针对上述问题,本文提供了一种兼容性解决方案。首先,...

    完整可行、浏览器兼容、通过测试的Iframe高度自适应程序

    - 一个常见的方法是利用`onload`事件监听Iframe的加载完成,然后通过JavaScript获取Iframe的contentDocument(或contentWindow.document,取决于浏览器兼容性)的body高度,将其设置为Iframe的高度。 2. **跨域...

    iframe的onload在Chrome/Opera中执行两次Bug的解决方法

    标题提到的问题是`iframe`的`onload`事件在这些浏览器中会被触发两次,这可能导致不必要的操作或逻辑错误。 这个问题的根本原因在于事件监听器的添加时机。在描述中给出的示例代码中,开发者首先创建了一个`iframe`...

    IE iframe的onload方法分析小结

    IE浏览器对`&lt;iframe&gt;`的`onload`事件支持并不直观,它需要通过`attachEvent`方法来注册事件处理函数,而不是像其他浏览器那样直接赋值给`onload`属性。这是因为IE使用的是事件模型的不同版本,即旧版的“事件冒泡”...

    js iframe 打印 打印预览 页眉页脚的设置

    为了确保iframe已经加载完成,我们通常会添加一个加载事件监听器: ```javascript iframe.onload = function() { iframe.contentWindow.focus(); // 避免某些浏览器的弹出窗口阻止策略 iframe.contentWindow....

    二个iframe之间传值 的小例子

    - `main.html`作为主页面,可能包含用于管理`iframe`间通信的代码,例如初始化`iframe`,设置`onload`事件监听,以及处理`iframe`间的`postMessage`通信。 总结,通过以上方式,我们可以实现在多个`iframe`之间灵活...

    JS判断iframe是否加载完成的方法

    在IE浏览器中,我们不能直接使用`onload`事件来监听`iframe`的加载完成,而是要依赖于`readyState`属性。当`readyState`属性的值变为"loaded"或"complete"时,表明`iframe`已经加载完毕。以下是一个示例代码: ``...

    判断iframe里的页面是否加载完成

    总之,通过监听`iframe`的`onload`事件,我们可以准确地知道`iframe`内的页面是否已加载完成,并根据需要执行相应的操作。在实际开发中,这一技巧对于实现动态加载、页面交互以及错误处理等功能非常关键。

    js实现iframe自适应高度

    3. **监听`load`事件**:当`iframe`内的页面加载完成后,我们可以通过`contentWindow`和`contentDocument`属性访问到`iframe`内的`window`和`document`对象,进而获取其内容的高度。 ```javascript iframe.onload...

    基于JS判断iframe是否加载成功的方法(多种浏览器)

    在Internet Explorer(IE)浏览器中,我们可以利用`iframe`元素的`onreadystatechange`事件来监听加载状态。`onreadystatechange`事件会在`iframe`内容发生变化时触发,包括加载中(`loading`)、加载完成(`loaded`...

    iframe自适应高度和宽度

    在上面的例子中,使用了`onload`事件来监听`iframe`内容加载完成,然后通过`document.all['myframe']`获取到`iframe`元素,并通过`myframe.document.body.scrollHeight`获取内部文档的实际高度,最后将这个高度设置...

    iframe实时高度检测

    - 在`iframe`加载完成后,例如在`window.onload`或`document.DOMContentLoaded`事件中,获取当前页面的总高度(包括滚动条)。 - 使用`parent.postMessage`方法向父页面发送一个自定义消息,包含`iframe`的实际...

    js操作iframe的一些知识

    6. **事件监听**:可以为iframe添加事件监听器,如`contentWindow.onload`来监听iframe内容加载完成,或`contentWindow.postMessage`监听其他窗口向iframe发送的消息。 7. **父子窗口通信**:JavaScript提供了`...

    iframe高度自适应.pdf

    - `handleFrameLoad()`: 主函数,用于监听`iframe`的加载事件,并在加载完成后执行高度调整。 - `getBodyHeight()`: 获取当前页面文档的高度。 - `getFrameHeight()`: 获取嵌入页面的高度。 - `initFrameHeight...

    iframe的onreadystatechange事件在firefox下的使用

    总之,对于跨浏览器的`iframe`加载事件处理,推荐使用`onload`事件,同时考虑使用`DOMContentLoaded`或`readystatechange`(在支持的浏览器中)作为补充,以确保在不同环境下都能正确地检测到`iframe`的加载状态。...

    iframe自适应高度说明文档

    实现`iframe`自适应高度主要依赖于JavaScript,通过监听`iframe`内部页面的加载事件,并动态调整`iframe`的高度。下面将详细介绍几种常见的实现方法: ##### 方法一:使用JavaScript函数SetWinHeight() ```...

Global site tag (gtag.js) - Google Analytics