原文地址:
http://dean.edwards.name/weblog/2005/09/busted/
我所谓的解决只是针对两款主要的浏览器——IE和Mozilla/Firefox。难道这还不够吗?
首先,让我先定义这个问题。程序员在他们的web应用程序的最开始调用window.onload 事件。对于菜单的动态效果或者更复杂的事情,如邮件系统的初始化,使用这个事件是很平常的。但是问题就在于onload事件会在所有页面内容(包括图片和其它二进制内容)被加载后才会触发。如果你的页面包含了大量的图片,那么你可能会发现在页面激活之前有一个显著的延迟。我们想做的就是寻找一种方法来确定DOM被完全的加载时不用等待所有那些讨厌的图片加载完毕。
Mozilla提供了一个特定的事件DOMContentLoaded。下面的代码准确的实现了在Mozilla平台下,我们想要解决的问题:
// Mozilla浏览器
if (document.addEventListener) ... {
document.addEventListener("DOMContentLoaded", init, false);
}
那么IE怎么办呢?
IE对
<script>标签
提供一个非常方便的属性:defer。这个属性的意义是指示IE延迟脚本的加载直到DOM被加载。但是这仅仅对外部脚本起作用。另一个重要的问题是,这个属性不能使用脚本动态的添加。这意味着,你不能使用DOM方法创建一个脚本并且设置defer属性,因为这会被忽略。
使用方便的defer属性,我们能够创建一个一小段脚本来调用我们的onload事件
< script defer src ="ie_onload.js" type ="text/javascript" ></ script >
这个外部脚本的内容仅仅是一行代码,用来调用我们的onload事件:
init();
使用这个方法,这就不算问题了。但是其他浏览器会忽略defer属性并立即加载脚本。这里有一些方法来绕过这个问题。我钟爱的方法是使用
条件注释对其他浏览器隐藏这段脚本。
<!-- [if IE]><script defer src="ie_onload.js"></script><![endif] -->
IE也支持
条件编译。下面的JavaScript代码和上面的HTML是等效的
// IE浏览器
/**/ /*@cc_on @*/
/**/ /*@if (@_win32)
document.write("<script defer src=ie_onload.js></script>");
/*@end @*/
到目前为止看起来一切顺利?我们现在需要支持其他的浏览器。我们只有一个选择——标准的window.onload事件:
// 其他浏览器
window.onload = init;
现在就剩下一个问题了(谁说这个问题简单?)。因为对于其他浏览器我们只能使用onload事件,但是对于IE和Mozilla,我们会调用两次init方法。为了避开这个问题,我们应当做一个标志方法,以至于onload事件只执行一次。所以我们的init方法会像下面的代码一样:
function init() ... {
//如果这个方法已经被调用过,就推出
if (arguments.callee.done) return;
//标志这个方法以至于我们不会做同样的事情两次
arguments.callee.done = true;
// 其他操作
} ;
And that, as they say, is that.
我提供了一个
例子,来证明这个技术。