【发布时间】:2010-11-20 13:43:00
【问题描述】:
加载 JavaScript 文件时是否会触发事件?问题出现是因为 YSlow 建议将 JavaScript 文件移动到页面底部。这意味着
$(document).ready(function1) 在包含 function1 代码的 js 文件加载之前触发。
如何避免这种情况?
【问题讨论】:
标签: javascript jquery
加载 JavaScript 文件时是否会触发事件?问题出现是因为 YSlow 建议将 JavaScript 文件移动到页面底部。这意味着
$(document).ready(function1) 在包含 function1 代码的 js 文件加载之前触发。
如何避免这种情况?
【问题讨论】:
标签: javascript jquery
我没有方便的参考,但是脚本标签是按顺序处理的,所以如果你把你的$(document).ready(function1)放在定义function1等的脚本标签之后的脚本标签中,你应该很好去。
<script type='text/javascript' src='...'></script>
<script type='text/javascript' src='...'></script>
<script type='text/javascript'>
$(document).ready(function1);
</script>
当然,另一种方法是通过在构建过程中组合文件来确保您总共只使用一个脚本标签。 (除非您从某个地方的 CDN 加载其他的。)这也将有助于提高您页面的感知速度。
编辑:刚刚意识到我实际上并没有回答你的问题:我认为没有触发跨浏览器事件,不。 如果你足够努力的话,见下文。您可以测试符号并使用 setTimeout 重新安排:
<script type='text/javascript'>
function fireWhenReady() {
if (typeof function1 != 'undefined') {
function1();
}
else {
setTimeout(fireWhenReady, 100);
}
}
$(document).ready(fireWhenReady);
</script>
...但如果您的脚本标签顺序正确,则不必这样做。
更新:如果您愿意,您可以获得动态添加到页面的script 元素的加载通知。要获得广泛的浏览器支持,您必须做两件不同的事情,但作为一种组合技术,这是可行的:
function loadScript(path, callback) {
var done = false;
var scr = document.createElement('script');
scr.onload = handleLoad;
scr.onreadystatechange = handleReadyStateChange;
scr.onerror = handleError;
scr.src = path;
document.body.appendChild(scr);
function handleLoad() {
if (!done) {
done = true;
callback(path, "ok");
}
}
function handleReadyStateChange() {
var state;
if (!done) {
state = scr.readyState;
if (state === "complete") {
handleLoad();
}
}
}
function handleError() {
if (!done) {
done = true;
callback(path, "error");
}
}
}
根据我的经验,错误通知 (onerror) 并非 100% 跨浏览器可靠。另请注意,某些浏览器会同时执行这两种机制,因此使用 done 变量来避免重复通知。
【讨论】:
$.getScript() api.jquery.com/jQuery.getScript
async 或 defer 属性来改变事情,否则 HTML 中的 script 标记会使页面加载停止,直到该脚本代码运行(因为它可能会输出带有document.write 的东西),所以您肯定知道较早的script 在以后的script 之前已加载(或失败)。 (浏览器很乐意并行下载脚本,但它们确保它们按照 HTML 中的顺序执行。)async 和 defer 改变了这一点。见:html.spec.whatwg.org/multipage/scripting.html#attr-script-async
当他们说“页面底部”时,他们并不是字面上的意思:他们的意思是在结束 </body> 标记之前。将您的脚本放在那里,它们将在 DOMReady 事件之前加载;之后放置它们,DOM 将在加载之前准备好(因为在解析结束 </html> 标记时它就完成了),正如您发现的那样,这将不起作用。
如果您想知道我是如何知道他们的意思的:我曾在 Yahoo!我们把我们的脚本放在</body>标签之前:-)
编辑:另见 T.J. Crowder 的回复,并确保您的物品顺序正确。
【讨论】:
看看 jQuery 的 .load() http://api.jquery.com/load-event/
$('script').load(function () { });
【讨论】:
进一步@T.J. Crowder 的回答,我添加了一个递归外循环,它允许遍历数组中的所有脚本,然后在加载所有脚本后执行一个函数:
loadList([array of scripts], 0, function(){// do your post-scriptload stuff})
function loadList(list, i, callback)
{
{
loadScript(list[i], function()
{
if(i < list.length-1)
{
loadList(list, i+1, callback);
}
else
{
callback();
}
})
}
}
当然,如果你愿意,你可以制作一个包装器来去掉“0”:
function prettyLoadList(list, callback)
{
loadList(list, 0, callback);
}
干得好@T.J. Crowder - 我对在其他线程中看到的“在运行回调之前添加几秒钟的延迟”感到畏缩。
【讨论】:
我总是从 JavaScript 文件的末尾调用来注册它的加载,它曾经对所有浏览器都非常适合我。
例如:我有一个 index.htm、Js1.js 和 Js2.js。我在 index.htm 头文件中添加函数 IAmReady(Id) 并使用文件末尾的参数 1 和 2 调用它,分别是 Js1 和 Js2。一旦从两个 js 文件中获得两次调用(将调用次数存储在静态/全局变量中),IAmReady 函数将具有运行启动代码的逻辑。
【讨论】:
更改脚本的加载顺序,以便在 ready 回调中使用之前定义 function1。
另外,我总是发现将 ready 回调定义为匿名方法然后命名为更好。
【讨论】:
function1 定义的文件,然后包含另一个使用该函数的文件。关键是你不能只是随机添加脚本文件。
像 T.J. wrote:顺序是定义的(至少当你的浏览器要执行任何JavaScript时它是连续的,即使它可能以某种方式并行下载脚本)。但是,显然您遇到了麻烦,也许您正在使用产生一些 404 Not Found 或超时的第三方 JavaScript 库?如果是,请阅读Best way to use Google’s hosted jQuery, but fall back to my hosted library on Google fail.
【讨论】: