33

我使用动态脚本加载来减少初始页面加载的持续时间。为了确保脚本定义的函数和对象是可访问的,我需要确保脚本已经完全加载。

为此,我开发了自己的 Javascript 库,因此对该主题进行了大量研究,研究它在不同库中是如何完成的。在与此问题相关的讨论中, LABjs的作者 Kyle Simpson表示:

LABjs(和许多其他加载器)在所有脚本元素上同时设置“onload”和“onreadystatechange”,知道有些浏览器会触发一个,有些会触发另一个......

在撰写本文时,您可以在当前版本的 jQuery v1.3.2中找到一个示例:

// Attach handlers for all browsers
script.onload = script.onreadystatechange = function(){
    if ( !done && (!this.readyState ||
    this.readyState == "loaded" || this.readyState == "complete") ) {
        done = true;
        success();
        complete();

        // Handle memory leak in IE
        script.onload = script.onreadystatechange = null;
        head.removeChild( script );
    }
};

这是最先进的技术,但是在分析 Opera 9.64 中的一个奇怪行为时,我得出的结论是,使用这种技术,onload 回调被过早地触发了。

我将发布我自己的发现来回答这个问题,并希望从社区收集进一步的证据和反馈。

4

5 回答 5

9

在 Opera 中,script.readyState 属性不可信。例如,readyState "loaded" 可能会在 Opera 9.64 中的脚本运行之前被触发。

我在 Opera 9.64 和 Opera 10 中进行了相同的测试,但结果不同。

在 Opera 9.64 中,onreadystatechange 处理程序被触发两次,一次在脚本运行之前和一次之后。在这两种情况下,readyState 属性都是“加载的”,这意味着不能信任该值来检测脚本加载的结束:

# Fri Dec 18 2009 17:54:43 GMT+0100
# Opera/9.64 (Windows NT 5.1; U; en) Presto/2.1.1
Test for script.readyState behavior started
Added script with onreadystatechange handler
readystatechange: loaded
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
readystatechange: loaded

在 Opera 10 中,onreadystatechange 处理程序仍然被触发两次,值为“loaded”,但两次都是在脚本运行之后:

# Fri Dec 18 2009 18:09:58 GMT+0100
# Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.10
Test for script.readyState behavior started
Added script with onreadystatechange handler
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
readystatechange: loaded
readystatechange: loaded

这些不同的行为表明 onreadystatechange 不是检测 Opera 中脚本加载结束的可靠方法。由于 Opera 还支持 onload 侦听器,因此应该使用这种其他机制。

根据这些测试的结果,onreadystatechange 应该只用于在 Internet Explorer 中检测脚本加载结束,而不应该在其他浏览器中设置。

于 2009-12-18T18:21:41.270 回答
4

在 Firefox、Safari 和 Chrome 中,onreadystatechange 处理程序永远不会被调用。

我创建了一个简短的测试用例,创建了一个只有 onreadystatechange 处理程序集的动态脚本:

<script type="text/javascript" language="javascript">
bezen.log.info(new Date(),true);
bezen.log.info(navigator.userAgent,true);

// Activate logs
bezen.log.on();
bezen.log.info('Test for script.readyState behavior started');

var script = document.createElement('script');
script.src = 'test1.js';
script.onreadystatechange = function(){
  bezen.log.info('readystatechange: '+script.readyState);
};
document.body.appendChild(script);
bezen.log.info('Added script with onreadystatechange handler');
</script>

我在 Firefox 2、Firefox 3、Firefox 3.5、Safari 3、Safari 4 和 Chrome 3 中对本地文件进行了测试,得到了类似的结果(这里是 FF 3.5 中记录的日志):

Fri Dec 18 2009 17:53:58 GMT+0100
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Test for script.readyState behavior started
Added script with onreadystatechange handler
test1.js: Start
test1.js: Start of closure
test1.js: End of closure

onreadystatechange 永远不会被调用。在这些浏览器中,只有 onload 侦听器可用于检测脚本加载的结束,不需要 onreadystatechange。

于 2009-12-18T18:01:13.527 回答
1

在 Internet Explorer 中,onreadystatechange 处理程序在脚本结束后按预期触发。

我在 Internet Explorer 6、Internet Explorer 7 和 Internet Explorer 8 中进行了相同的测试,在这三个浏览器中得到了相似的结果(这里是 Internet Explorer 6 中记录的日志):

Fri Dec 18 18:14:51 UTC+0100 2009
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Test for script.readyState behavior started
Added script with onreadystatechange handler
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
readystatechange: complete

这里使用本地文件进行测试,readyState 总是“完成”,刷新几次页面后还是一样。

然而,正如Nicholas C. Zakas 在这篇文章中所指出的,您也可以在不同的情况下观察到“加载”和“完成”,或者只是“加载”。

于 2009-12-18T18:11:15.317 回答
1

我发现 Internet Explorer(在 9 中测试)并不总是在 readyState === 'loaded' 时准备好你的脚本。我已经成功使用了这个事件处理程序(当然是 9 个)onactivate。之前一直在拔头发。

于 2012-03-23T15:34:55.293 回答
0

Chrome 中的类似结果。

不用准备...

只是onload和onerror。

于 2012-07-12T18:40:16.130 回答