判断页面加载完毕进行相关操作是经常碰到的问题 ,不能仅仅满足于 window.onload 或 <body οnlοad="">。
<html>
<head>
<title>页面加载问题-onload-by yiminghe</title>
<script>
function ready() {
alert(document.getElementById('i_test').value);
}
var addE = function() {
if (window.addEventListener) {
return function(el, eventName, fn, capture) {
el.addEventListener(eventName, fn, (capture));
};
} else if (window.attachEvent) {
return function(el, eventName, fn, capture) {
el.attachEvent("on" + eventName, fn);
};
} else {
return function() {
};
}
} ();
/*
标准DOM2事件添加,ie,ff兼容 ,等页面image、flash、iframe等都载入完才运行 ,
在如下的 html中 若不能上126 则 等很长时间才有弹出框呢。
等同于 window.οnlοad=ready;
*/
/*
addE(window,'load',function() {
ready();
});
*/
/*
ff 都不行,可见 body 没有 onload 事件
ie 报错,还没有 document.body 形成
*/
/*
addE(document.body,'load',function() {
ready();
});
*/
/**
最快的方法,我们不必等待页面image、flash、iframe等都载入完(若要获取图片属性则不要用),只要htmlDOM形成就可以了,
而这个各个浏览器有不同实现,ff 遵循了标准,而ie则没有遵循标准
在下面的例子中 ,即使上不了 126 ,也会很快提示页面载入完成
**/
function DOMOk(readyOk) {
/* for Mozilla */
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", readyOk, false);
}
/* for Internet Explorer */
if (window.attachEvent) {
document.write("<s" + 'cript id="ie-deferred-loader" defer="defer" src="/' + '/:"></s' + "cript>");
var defer = document.getElementById("ie-deferred-loader");
defer.onreadystatechange = function() {
if (this.readyState == "complete") {
readyOk();
}
};
}
}
DOMOk(ready);
</script>
</head>
<body>
<!--
//ie ff 兼容,非常老式,在dom2标准出现之前就有了
οnlοad="ready()"
-->
<input id="i_test" value="1" />
<img src='http://mimg.126.com/logo/126logo_tsp.gif' />
</body>
<script>
/*
只有 ie 可以 ,可能为了兼容标准以前的ie版本,且不能放在 head 中 ,document.body尚不存在
*/
// document.body.οnlοad=ready;
/*
ie,ff 都不行,可见 body 没有 onload 事件
*/
/*
addE(document.body,'load',function() {
ready();
});
*/
</script>
</html>
这个问题这么复杂,那么 javascript 库肯定会考虑 ,看看 extjs 的实现吧,Ext.onReady 就不解释了,原理和上相同。需要注意的是 defer 属性并不能通过动态添加脚本标签设置,而只能在页面初始阶段通过 document.write 设置!
这里使用了 script 的 defer 属性,但是这种方法有个问题,当页面包含iframe时,必须得等该页面的 iframe 载入完毕后才会触发 defer 的脚本执行。所以更好的方法是 doScroll hack :
function IEContentLoaded (w, fn) { var d = w.document, done = false, // only fire once init = function () { if (!done) { done = true; fn(); } }; // polling for no errors (function () { try { // throws errors until after ondocumentready d.documentElement.doScroll('left'); } catch (e) { setTimeout(arguments.callee, 50); return; } // no errors, fire init(); })(); // trying to always fire before onload d.onreadystatechange = function() { if (d.readyState == 'complete') { d.onreadystatechange = null; init(); } }; }
不过这种解法仍然有问题,当页面处于 iframe 中时,doScroll 实际上不会抛错 :
顶层:
<script>
try{
document.documentElement.doScroll("left");
alert(2);
}catch(e){
alert(3);
}
</script>
<iframe src='http://yiminghe.xx.com/t/inner.html'/>
iframe :
<script> alert(window.top==window); //alert(window.frameElement); document.documentElement.doScroll("left"); alert(1); </script> <img src='sleep.jsp'/>
所以只能在页面在顶层时使用:
// polling for no errors
if(window==window.top)(function () { try { // throws errors until after ondocumentready d.documentElement.doScroll('left'); } catch (e) { setTimeout(arguments.callee, 50); return; } // no errors, fire init(); })();
最不好也可以用 onreadystatechange ,其complete 仍然会比window load 快,但是依然在 img load 后出发 (dean's test )。
另一点是关于使用 window.top == window 还是使用 window.frameElement 来判断是否是顶层窗口,如果使用 window.frameElement 的话还得注意 try catch 否则如果iframe和主页面不同域会报错,所以一般还是用 window.top==window好一点?
update : 2010-12-26
还是使用 window.frameElement 好,否则 ie6 下会有问题,iframe中页面如果在设置 domain 前访问了 top ,则即使 iframe 的 domain 和 外层页面一致也无法访问 window.frameElement了 :jquery ticket 4787
PS:ie9 quirks 模式并不能模拟 ie6 这种bug,quirks 应该是 5.5
结果图: