我们有时可能需要一些在页面加载完成之后执行的方法,其实js原生就提供了onload方法,所以我们最简单的办法就是直接给onload赋值一个函数,在页面加载完成之后就会自动执行
widnow.onload = function(e) { // do some things }
或者我们也可以使用addEventListener,来监听多个load事件,此处我们先暂时不考虑低版本的ie
document.addEventListener("load", function(e) { // do some things }, false);
这个时候,我们考虑到,明显jq的ready方法更好使用,而且简单很多,所以我们也可以通过切片编程的方法来实现一个onload方法
// after切片,可以在方法之后增加after,顺序执行 Function.prototype.after = function(func) { var _self = this; // 返回一个新的可执行方法 return function() { var ret = _self.apply(this, arguments); // 当上一个方法返回false,则不会执行下一个after if(ret === false) { return false; } func.apply(this, arguments); return ret; } } $ = function(func) { window.onload = (window.onload || function() {}).after(func); } // 使用 $(function() {}); $(function() {});
当然,感觉可能实现还不如无限次的调用addEventListener,虽然这个方法可以保证执行顺序
而实际上,你在使用该方法的时候,会发现这个方法始终会在jq的ready之后执行,是因为jq的ready实现,是监听了DOMContentLoaded,这个事件是在文档加载完成之后就会触发,而不必等待所有的资源包括异步资源加载完成。所以我们最后的实现可以这样
var $ = (function() { var funcs = []; // 保存所有需要执行的方法 var ready = false; // 页面准备完毕之后,修改为true // 当文档处理完毕,调用事件处理程序 function handler(e) { // 如果执行过了,直接返回 if(ready) { return; } // 如果发生过readysyayechange事件,但是状态不为complete,那么文档没有准备好 if(e.type === "readystatechange" && document.readyState !== "complete") { return; } // 运行所有注册函数 for(var i = 0; i < funcs.length; i++) { funcs[i].call(document); } // 设置ready为true,并移除所有方法 ready = true; funcs = null; } // 为接收到的任何事件注册处理程序 if(document.addEventListener) { document.addEventListener("DOMContentLoaded", handler, false); document.addEventListener("readystatechange", handler, false); document.addEventListener("load", handler, false); } else if(document.attachEvent) { // 处理ie兼容 document.attachEvent("onreadystatechange", handler); document.attachEvent("onload", handler); } // 返回__whenReady函数 return function __whenReady(f) { if(ready) { f.call(document); } else { funcs.push(f); } } }());
核心就是在判断几个加载事件执行的时候,文档是否准备好,如果没有准备好,则将需要执行的函数按照顺序缓存起来,然后在监听到文档准备好之后依次执行。