javascript 加载javascript异步,然后在执行回调之前检查加载的DOM
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4249030/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Load javascript async, then check DOM loaded before executing callback
提问by Ken
Problem:
Load js files asynchronously, then check to see if the dom is loaded before the callback from loading the files is executed.
问题:
异步加载js文件,然后在执行加载文件的回调之前检查dom是否加载。
edit:We do not use jQuery; we use Prototype.
edit:added more comments to the code example.
编辑:我们不使用 jQuery;我们使用原型。
编辑:在代码示例中添加了更多注释。
I am trying to load all of my js files asynchronously so as to keep them from blocking the rest of the page. But when the scripts load and the callback is called, I need to know if the DOM has been loaded or not, so I know how to structure the callback. See below:
我试图异步加载我的所有 js 文件,以防止它们阻塞页面的其余部分。但是当脚本加载并调用回调时,我需要知道 DOM 是否已加载,所以我知道如何构造回调。见下文:
//load asynchronously
(function(){
var e = document.createElement('script');
e.type = "text/javascript";
e.async = true;
e.src = srcstr;
// a little magic to make the callback happen
if(navigator.userAgent.indexOf("Opera")){
e.text = "initPage();";
}else if(navigator.userAgent.indexOf("MSIE")){
e.onreadystatechange = initPage;
}else{
e.innerHTML = "initPage();";
}
// attach the file to the document
document.getElementsByTagName('head')[0].appendChild(e);
})();
initPageHelper = function(){
//requires DOM be loaded
}
initPage = function(){
if(domLoaded){ // if dom is already loaded, just call the function
initPageHelper();
}else{ //if dom is not loaded, attach the function to be run when it does load
document.observe("dom:loaded", initPageHelper);
}
}
The callback gets called properly due to some magic behind the scenes that you can learn about from this Google talk: http://www.youtube.com/watch?v=52gL93S3usU&feature=related
由于您可以从这个 Google 演讲中了解到一些幕后魔法,回调被正确调用:http: //www.youtube.com/watch?v=52gL93S3usU& feature=related
What's the easiest, cross-browser method for asking if the DOM has loaded already?
询问 DOM 是否已经加载的最简单的跨浏览器方法是什么?
EDIT
Here's the full solution I went with.
I included prototype and the asynchronous script loader using the normal method. Life is just so much easier with prototype, so I'm willing to block for that script.
编辑
这是我使用的完整解决方案。
我使用普通方法包含原型和异步脚本加载器。有了原型,生活就轻松多了,所以我愿意为那个脚本而阻止。
<script type="text/javascript" src="prototype/prototype.js"></script>
<script type="text/javascript" src="asyncLoader.js"></script>
And actually, in my code I minified the two files above and put them together into one file to minimize transfer time and http requests.
实际上,在我的代码中,我缩小了上面的两个文件并将它们放在一个文件中,以最大限度地减少传输时间和 http 请求。
Then I define what I want to run when the DOM loads, and then call the function to load the other scripts.
然后我定义了我想在 DOM 加载时运行什么,然后调用该函数来加载其他脚本。
<script type="text/javascript">
initPage = function(){
...
}
</script>
<script type="text/javascript">
loadScriptAsync("scriptaculous/scriptaculous.js", initPage);
loadScriptAsync("scriptaculous/effects.js", initPage);
loadScriptAsync("scriptaculous/controls.js", initPage);
...
loadScriptAsync("mypage.js", initPage);
</script>
Likewise, the requests above are actually compressed into one httpRequest using a minifier. They are left separate here for readability. There is a snippet at the bottom of this post showing what the code looks like with the minifier.
同样,上面的请求实际上使用压缩器压缩成一个 httpRequest。为了便于阅读,它们在这里分开。这篇文章底部有一个片段,显示了使用 minifier 时的代码。
The code for asyncLoader.js is the following:
asyncLoader.js 的代码如下:
/**
* Allows you to load js files asynchronously, with a callback that can be
* called immediately after the script loads, OR after the script loads and
* after the DOM is loaded.
*
* Prototype.js must be loaded first.
*
* For best results, create a regular script tag that calls a minified, combined
* file that contains Prototype.js, and this file. Then all subsequent scripts
* should be loaded using this function.
*
*/
var onload_queue = [];
var dom_loaded = false;
function loadScriptAsync(src, callback, run_immediately) {
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = src;
if("undefined" != typeof callback){
script.onload = function() {
if (dom_loaded || run_immediately)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
};
script.onreadystatechange = function() {
if (script.readyState == 'complete'){
if (dom_loaded || run_immediately)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
}else if(script.readyState == 'loaded'){
eval(script);
if (dom_loaded || run_immediately)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
}
};
}
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
}
document.observe("dom:loaded", function(){
dom_loaded = true;
var len = onload_queue.length;
for (var i = 0; i < len; i++) {
onload_queue[i]();
}
onload_queue = null;
});
I added the option to run a script immediately, if you have scripts that don't rely on the page DOM being fully loaded.
如果您的脚本不依赖于完全加载的页面 DOM,我添加了立即运行脚本的选项。
The minified requests actually look like:
缩小的请求实际上看起来像:
<script type="text/javascript" src="/min/?b=javascript/lib&f=prototype/prototype.js,asyncLoader.js"></script>
<script type="text/javascript"> initPage = function(e){...}</script>
<script type="text/javascript">
srcstr = "/min/?f=<?=implode(',', $js_files)?>";
loadScriptAsync(srcstr, initPage);
</script>
They are using the plugin from: [http://code.google.com/p/minify/][1]
他们使用的插件来自:[ http://code.google.com/p/minify/][1]
采纳答案by gblazex
What you need is a simple queueof onloadfunctions. Also please avoid browser sniffingas it is unstable and not future proof. For full source code see the [Demo]
您需要的是一个简单的函数队列onload。也请避免浏览器嗅探,因为它不稳定且不能保证未来的发展。完整源代码见[演示]
var onload_queue = [];
var dom_loaded = false;
function loadScriptAsync(src, callback) {
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = src;
script.onload = script.onreadystatechange = function() {
if (dom_loaded)
callback();
else
onload_queue.push(callback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
};
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
}
function domLoaded() {
dom_loaded = true;
var len = onload_queue.length;
for (var i = 0; i < len; i++) {
onload_queue[i]();
}
onload_queue = null;
};
// Dean's dom:loaded code goes here
// do stuff
domLoaded();
Test usage
测试使用
loadScriptAsync(
"http://code.jquery.com/jquery-1.4.4.js",
function() {
alert("script has been loaded");
}
);
回答by rob
You can always put your initial loader script at the bottom, right before the closing body tag.
您始终可以将初始加载程序脚本放在底部,就在结束正文标记之前。

