Ruby's Louvre

The Crankiness of Belief achieves Great , not the Trick of Regulation.

javascript的domReady

基本上每个库都有这东西,因为如果要对页面上的元素进行操作,我们必须等到页面加载了这个元素才行,否则会报错,但是我们很能判定某个元素是否已加载,但我们可以判定页面是否加载,这就是我们经常把代码放到window.onload = function(){}之中的缘由。但window.onload事件是待到页面上的所有资源被加载才激活,如果页面上有许多图片,音乐或falsh,而我们要操作的元素在的它们的下方呢?因此,W3C做了少有几桩好事,搞了DOMContentLoaded与addEventListener,可能也不是他们搞的,把某浏览器的私有实现盖上个大印,标明它是标准罢了,如safari的canvas,IE的getBoundingClientRect……DOMContentLoaded是DOM树完成时激活的事件,addEventListener支持多重加载与冒泡捕获。IE没有这东西,我在《javascript的事件加载》基本给出它的雏形了(注:昨天重写了该文),本文将在它的基础上进行进一步的封装与改进,如setTimeout改为零秒延迟,清除setTimeout,执行完加载后把加载函数清除掉,对IE框架结构的页面进行更安全的设置……最重要的是修正下面网友 wbkt2t 提到的在IE中的失误!晚上再次更新,发现doScroll并没有想象中的快,比不上script defer……

IE6的数据:

IE8的数据:

综合执行顺序为:

  1. oncontentready,这时DOM树完成
  2. script defer,这时开始执行设定了defer属性的script
  3. ondocumentready complete,这时可以使用HTC组件与XHR
  4. html.doScroll 这时可以让HTML元素使用doScroll方法
  5. window.onload 这时图片flash等资源都加载完毕
      new function(){
        dom = [];
        dom.isReady = false;
        dom.isFunction = function(obj){
          return Object.prototype.toString.call(obj) === "[object Function]";
        }
        dom.Ready = function(fn){
          dom.initReady();//如果没有建成DOM树,则走第二步,存储起来一起杀
          if(dom.isFunction(fn)){
            if(dom.isReady){
              fn();//如果已经建成DOM,则来一个杀一个
            }else{
              dom.push(fn);//存储加载事件
            }
          }
        }
        dom.fireReady =function(){
          if (dom.isReady)  return;
          dom.isReady = true;
          for(var i=0,n=dom.length;i<n;i++){
            var fn = dom[i];
            fn();
          }
          dom.length = 0;//清空事件
        }
        dom.initReady = function(){
          if (document.addEventListener) {
            document.addEventListener( "DOMContentLoaded", function(){
              document.removeEventListener( "DOMContentLoaded", arguments.callee, false );//清除加载函数
              dom.fireReady();
            }, false );
          }else{
            if (document.getElementById) {
              document.write("<script id=\"ie-domReady\" defer='defer'src=\"//:\"><\/script>");
              document.getElementById("ie-domReady").onreadystatechange = function() {
                if (this.readyState === "complete") {
                  dom.fireReady();
                  this.onreadystatechange = null;
                  this.parentNode.removeChild(this)
                }
              };
            }
          }
        }
      }

使用方法:

      dom.Ready(function(){
        alert("我的domReady!")
      });
      dom.Ready(function(){
        alert("我的domReady测试多重加载1!")
      });
      dom.Ready(function(){
        alert("我的domReady测试多重加载2!")
      });
      dom.Ready(function(){
        alert(document.getElementById("test").innerHTML)
      });

以下,更新前一些有用的东西,舍不得丢弃,暂时还留着。

用于判定是否为顶层window:

      //方法一
      var topwindow = self === self.top
      //方法二
      var topwindow = false;
      try {
        topwindow = window.frameElement == null;
      } catch(e){}

推荐第一种,第二种必须要待到document.body形成之时才能用,有关frameElement 的资料详见这里这里。 frameElement要求当前window是一个 frame 或 iframe才存在,否则返回null。不过第一种要注意一下,在IE 下,top, self, parent 和对应的 window 并不全等。见下面测试:

一些有用的链接:

http://p2b.jp/200805-events-order

http://tanny.ica.com/ICA/TKO/test.nsf/DOMContentLoaded.htm

标签: javascript

posted on 2009-12-30 13:29 司徒正美 阅读(3639) 评论(12) 编辑 收藏

评论

#1楼 2009-12-30 13:55 wbkt2t      

学习了
我之前也探讨过jq的ready,发现jq的ready是先于onload的
而我用lz的ready
window.onload = function(){
alert("onload")
}
dom.Ready(function(){
alert("NetReady")
});

发现是先执行onload,再执行dom.Ready的,而qj同样代码好像先于onload发生。如知道如何实现?还是我测试有问题
望指导
 回复 引用 查看   

#2楼 2009-12-30 13:55 goodsun      

if (!+"\v1") {
------------
这是?

document.write("<script id=__ie_onload defer src=//0><\/scr"+"ipt>");
script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") dom.fire();


};
 回复 引用 查看   

#3楼 2009-12-30 14:26 小火      

if (!+"\v1") {
------------
这是?
}
我也想问楼上的问题
 回复 引用 查看   

#4楼 2009-12-30 14:40 Tmac_      

@小火
在IE中
 回复 引用 查看   

#5楼 2009-12-30 15:28 小火      

我刚才在几个浏览器里测试了下,确实是这样,这么写有没什么历史原因???他比if(document.all)更快?或者"\v1"表示什么对象?  回复 引用 查看   

#6楼[楼主] 2009-12-30 15:47 司徒正美      

@wbkt2t
谢谢你的提醒,已修复
 回复 引用 查看   

#7楼 2009-12-30 16:04 Tony Zhou      

good  回复 引用 查看   

#8楼 2009-12-31 15:01 Dreampuf      

(!+"\v1").......
学习了...这个应该是区别IE还是FF的代码."+"表示转换为数字,"!"表示转换为布尔值."+"和parseInt很相似,在IE中碰到"\v"就返回NaN了,但在FF中却一直读完,返回1,所以造成了返回false("!"非运算了一次).
 回复 引用 查看   

#9楼 2010-01-08 11:27 瓜籽      

我在IE8中测试了一下,发现通过document.write("<script id=__ie_onload defer src=//0><\/scr"+"ipt>");写入到页面中的script并没有被删去。仍然留在页面中,而且还是3个。是我测试的问题吗?

 回复 引用 查看   

#10楼 2010-12-02 12:47 chenxumi      

现在很多页面都是由js动态生成,这与页面元素的顺序和个数紧密相关,加入的scripts改变了页面结构,dom.fireReady()中的执行结果有可能不是预期的,建议这样:
this.onreadystatechange = null;
this.parentNode.removeChild(this);
dom.fireReady();


 回复 引用 查看   

#11楼 2011-07-04 15:57 风子2      

我想问下,直接new function(){
//codes
};
里面的codes代码会执行?这种语法是什么意思?第一次见,加new 关键字有什么用?
 回复 引用 查看   

#12楼 2011-10-12 15:19 shirne      

@风子2
别的我不太懂,使函数运行还知道几个。
new关键字会把后面函数当作构造函数。
new的时候会运行构造函数的内容
还有几种方式
(function(){
//code,这个是最常见的匿名函数
})();

~function(){
//code,这样也可以运行
}()
下面那种方式还可以延伸,比如前面符号换成 !,+,-都可以
 回复 引用 查看