标准参考
关于 HTML 4.01 规范中 onload 内在事件说明:http://www.w3.org/TR/html401/interact/scripts.html#adef-onload
关于 DOM Level2 Events 规范中 load 事件说明:http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-htmlevents
问题描述
插入空白页面 IFRAME 元素时 Chrome Safari Opera 浏览器中会触发 load 事件。
造成的影响
Chrome Safari Opera 浏览器中,在插入 "src" 属性未设置或设置为空字符串的 IFRAME 元素后立即修改其 "src" 属性值,会导致 IFRAME 标记绑定的 load 事件再被触发。
触发 IE 和 Firefox 浏览器中 IFRAME 标记 "src" 属性值的历史记录问题。
受影响的浏览器
所有浏览器 |
---|
问题分析
根据规范说明,load 事件可以作用于 FRAMESET 标记中,因此同样也可以作用于 IFRAME 标记上。它表示着 IFRAME 内页面已经完全加载完毕 IFRAME 标记处于可用状态。
如果创建的 IFRAME 标记没有设定 "src" 属性和值,那么他的 load 事件是否会被触发呢?
分析以下代码:
function iframeLoad(){ alert("IFRAME 标记的 onload 事件触发"); } function addEvent(eventName,element,fn){ if (element.attachEvent) element.attachEvent("on"+eventName,fn); else element.addEventListener(eventName,fn,false); } window.onload = function (){ var iframe = document.createElement('iframe'); addEvent("load",iframe,iframeLoad); document.body.appendChild(iframe); iframe.src="iframe.html"; }
代码中动态创建了 IFRAME 标记,为其监听 load 事件,此时还没有显示性的设定他的 "src" 值就将他加入文档树中。此后立即为 IFRAME 设置 "src" 属性,指向具体的 URL 。
观察各浏览器中运行效果:
IE Firefox | Chrome Safari Opera | |
---|---|---|
IFRAME onload 事件触发次数 | 1次 | 2次 |
看到 Chrome Safari Opera 中 onload 事件被触发两次。
为了搞清这个问题,我们需要知道没有设置 src 属性的 IFRAME 被添加到 DOM 树中后,其默认的 "src" 值是什么。继续分析以下代码:
function iframeLoad(iframe){ document.body.appendChild(document.createTextNode("IFRAME URL : "+iframe.location.href)); } function addEvent(eventName,element,fn){ if (element.attachEvent) element.attachEvent("on"+eventName,fn); else element.addEventListener(eventName,fn,false); } window.onload = function (){ var iframe = document.createElement('iframe'); addEvent("load",iframe,function (){iframeLoad(iframe.contentWindow)}); document.body.appendChild(iframe); }
所有浏览器 | |
---|---|
URL | about:blank |
这 里将空 src 值 IFRAME 标记内调用页面的 URL 打印出来,可以发现所有浏览器中处理一致,均为 "about:blank" 。这个页面是个空 HTML 文档页,所有浏览器均内置提供。这个空页面被加载完成后同样会触发 IFRAME 标记的 onload 事件。
由 此可以推测,是否由于 Chrome Safari Opera 浏览器中当 IFRAME 标记被插入文档树后,载入"about:blank" 页面速度非常快立即就触发了 load 事件,以至于还没来得及执行为他指定新 URL 的语句;当新 URL 指定后,当前页面加载完成时又触发了一次 IFRAME 标记的 load 事件,这样总共就触发了两次。
而其他浏览器则是载入 "about:blank" 页花费时间加多,页面并未完全加载时其 URL 已经被改变,因此相对 Chrome Safari Opera 浏览器第一次 load 事件并没有触发,只有等新 URL 中的页面加载完毕后 load 事件才被触发,这样总共只触发了一次。
为了证明以上猜测,将一段延时执行的代码加入其中:
function iframeLoad(iframe){ var src = (iframe.src)?iframe.src:iframe.contentWindow.location.href; document.body.appendChild(document.createElement("br")); document.body.appendChild(document.createTextNode("IFAME 标记 src 值为 "+ src + " 的 onload 事件触发")); } function addEvent(eventName,element,fn){ if (element.attachEvent) element.attachEvent("on"+eventName,fn); else element.addEventListener(eventName,fn,false); } window.onload = function (){ var iframe = document.createElement('iframe'); addEvent("load",iframe,function (){iframeLoad(iframe)}); iframe.src= "about:blank"; document.body.appendChild(iframe); setTimeout(function (){iframe.src="iframe.html";},300) }
代码中,在 IFRAME 节点被添加到文档树树后,延时 300 毫秒再改变它的 src 属性,以便给其他浏览器充足的事件将 "about:blank" 页面加载完成。
此时各浏览器执行结果一致:
所有浏览器 | |
---|---|
IFRAME onload 事件触发次数 | 'about:blank' 页面1次 'iframe.html' 页面1次 共2次 |
这 个结果证明了之前的猜想:Chrome Safari Opera 浏览器执行速度比想象中的要快的多,导致好像“多”触发了一次 load 事件,实际上所有浏览器均会为 IFRAME 标记内每个页面触发他的 load 事件,前提是让这些页面有足够的事件加载完成。
【注】:使用appendChild 方法将没有设置 src 属性的 IFRAME 插入文档树和使用 innnerHTML 方式将没有写 src 属性的 IFRAME 标记字符串插入文档树时,均会存在以上分析的问题。
最后来看,上面的代码中在插入节点之前为 IFRAME 标记显式性的设置了 src 属性值为 "about:blank" 的页面,这是为了避免 Firefox 中 URL 缓存的 Bug。
在 IE 和 Firefox 中,如果 IFRAME 没有显示性的设置 "src" ,如果该页面第一次被打开,则实际页面的 src 值为 "about:blank";否则将为 IFRME 标记中最后一个被设定的 "src" 值。
分析以下代码(没有明确定义 src 属性值):
window.onload = function (){ var iframe = document.createElement('iframe'); addEvent("load",iframe,function (){iframeLoad(iframe)}); document.body.appendChild(iframe); setTimeout(function (){iframe.src="iframe.html";},300) }
注意代码 "src" 值最初未定义,最终被修改为 "ifarme.html",观察在各浏览器中运行结果:
Chrome Safari Opera | IE Firefox | |
---|---|---|
首次显示 | 'about:blank' 页 onload 事件触发 'iframe.html' 页 onload 事件触发 | |
F5 普通刷新 | 'about:blank' 页 onload 事件触发 'iframe.html' 页 onload 事件触发 | 'iframe.html' 页 onload 事件触发 'iframe.html' 页 onload 事件触发 |
Ctrl + F5 强制刷新 | 'about:blank' 页 onload 事件触发 'iframe.html' 页 onload 事件触发 |
可见,IE 和 Firefox 浏览器中出现了 IFRAME 元素的 "src" 属性最后指向的记忆问题,普通刷新页面后,没有明确设置 "src" 属性的 IFRAME 标记将默认使用上一条历史记录中的 "src" 值。
解决方案
为 IFRAME 标签的 src 属性指定具体 URL 后再将节点插入 DOM 树中 。
本文转自:http://www.w3help.org/zh-cn/causes/SD9019