昨天遇到了一个问题,就是想在外部js里实现页面加载完了就执行js,类似于jquery中$或者window.onload,当然和window.onload还有所不同,因为window.onload是在图片啊其他的资源加载完了才开始执行的,而我想在尽可能快的执行,问题可以用一段代码来描叙:
1 <html> 2 <head> 3 <script src="test.js“></script> 4 5 </head> 6 <body> 7 8 <p id="hehe"></p> 9 10 </body> 11 12 </html>
test.js代码:
alert(document.getElementById("hehe");
经过执行之后我们发现,alert返回null,按理应该返回object才对,因为我们alert了一个对象,之所以会产生这种情况,就是js先于domtree的建立,解决这种问题,当然一般我们可以换种写法,用window.onload,也可以按照yahoo优化网站的35条建议把<script>放到代码底部。也可以在外部js里强行僵化的用setTimeout(fn,seconds)来设置时间,但是博主不想这样,想一劳永逸并且尽可能优雅的解决问题,求助之后大神给了如下代码:
1 var isReady = false; 2 var readyList = []; 3 4 function ready(fn) { 5 if (isReady) { 6 setTimeout(function () { 7 fn() 8 }, 0); 9 return; 10 } 11 readyList.push(fn); 12 } 13 14 function setReady() { 15 if (isReady) { 16 return; 17 } 18 19 isReady = true; 20 for (var i = 0, n = readyList.length; i < n; i++) { 21 readyList[i](); 22 } 23 readyList.length = 0; 24 } 25 26 (function () { 27 if (document.readyState === ‘complete‘) { 28 setTimeout(setReady, 0); 29 } else { 30 document.addEventListener(‘DOMContentLoaded‘, setReady); 31 window.addEventListener(‘load‘, setReady); 32 } 33 }());
我一开始没有理解异步回调,所以百思不得其解,后来有幸有人告诉我这是回调这才豁然开朗,其实setTimeout()这不也是一种异步回调的写法吗?
上述代码中
document.addEventListener(‘DOMContentLoaded‘, setReady); window.addEventListener(‘load‘, setReady);这就是异步回调addEventListener和setTimeout都是异步回调的写法,在setTimeout中setTimeout(fn,seconds)但seconds条件未满足即此条件未触发时,js程序挂起,页面构建domtree渲染,但事件发生之后,中断页面渲染,执行js代码,类似的可以理解addEventListener函数。异步回调之后就可以达到我们的目的
这就是异步回调 addEventListener和setTimeout都是异步回调的写法,在setTimeout中setTimeout(fn,seconds)但seconds条件未满足即此条件未触发时,js程序挂起,页面构建domtree渲染,但事件发生之后,中断页面渲染,执行js代码,类似的可以理解addEventListener函数。 异步回调之后就可以达到我们的目的,大神给的代码中,有队列还有全局标记,功能很多,处理很全面,为了理解核心部分,我做一个简化,html代码不变,js代码改为:
1 //var isReady = false; 2 //var readyList = []; 3 var a={}; 4 5 function ready(fn) { 6 //if (isReady) { 7 // setTimeout(function () { 8 // fn() 9 // }, 0); 10 // return; 11 // } 12 a.fn=fn; 13 } 14 15 function setReady() { 16 a.fn(); 17 } 18 19 (function () { 20 if (document.readyState === ‘complete‘) { 21 setTimeout(setReady, 0); 22 } else { 23 document.addEventListener(‘DOMContentLoaded‘, setReady); 24 window.addEventListener(‘load‘, setReady); 25 } 26 }());
这样注释掉一些部分理解起来就容易了,但是请注意,这样因为有两个事件和两个回调会执行两次fn()。
关于回调还可以参加阮一峰老师的博客,写的很好http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html 我打算转载过来。