JavaScript的document.write内联脚本执行顺序

36

我有以下脚本,第一个和第三个 document.writeline 是静态的,第二个是动态生成的

<script language="javascript" type="text/javascript">
document.write("<script language='javascript' type='text/javascript' src='before.js'><\/sc" + "ript>");
document.write("<script language='javascript' type='text/javascript'>alert('during');<\/sc" + "ript>");
document.write("<script language='javascript' type='text/javascript' src='after.js'><\/sc" + "ript>");
</script>

Firefox 和 Chrome 会先显示 before, duringafter,而 Internet Explorer 则会先显示during,之后才显示 beforeafter

我发现了一篇文章,它指出我并不是第一个遇到这个问题的人,但这并没有让我感觉更好。

是否有人知道如何在所有浏览器中确定顺序,或者以某种方式修改 IE,使其像其他正常的浏览器一样工作?

注意事项: 代码片段是一个非常简单的重现。它是在服务器上生成的,第二个脚本是唯一改变的部分。它是一个很长的脚本,之所以在它前后有两个脚本,是为了让浏览器缓存它们,使得代码的动态部分尽可能小。它也可能在同一页中多次出现,带有不同的生成代码。


必读:http://html5rocks.com/en/tutorials/speed/script-loading - Pacerier
7个回答

24
不,这是Internet Explorer的行为。
如果动态附加脚本,IE、Firefox和Chrome都会以异步方式下载脚本。
Firefox和Chrome将等待所有异步请求返回,然后按它们在DOM中附加的顺序执行脚本,但IE将按照从网络返回的顺序执行脚本。
由于警报需要“检索”的时间比外部javascript文件短,这可能可以解释您所看到的行为。
来自Kristoffer Henriksson关于异步脚本加载的主题帖
在这种情况下,IE和Firefox都会下载两个脚本,但Internet Explorer还会按照它们完成下载的顺序执行它们,而Firefox会异步下载它们,但仍会按照它们在DOM中附加的顺序执行它们。
在Internet Explorer中,这意味着您的脚本不能相互依赖,因为执行顺序将因网络流量、缓存等而异。
考虑使用Javascript加载器。它将让您指定脚本依赖项和执行顺序,同时也异步加载您的脚本以提高速度,并平滑一些浏览器之间的差异。
这是一个很好的概述五个顶级脚本加载器

我曾经使用过RequireJS和LabJS。在我看来,LabJS的观点稍微少一些。


使用async=false怎么样?https://dev59.com/12ox5IYBdhLWcg3wvW3X#56639925 - Flion
@Flion,你可以这样做,但你可能会发现它会大大增加页面请求到功能页面的时间。这取决于所请求资源的大小和数量以及互联网连接的延迟和吞吐量等情况。总的来说,利用异步调用是一件好事。这个问题有点老了,可能在未来微软将采用Chromium作为Edge引擎,因此浏览器行为在这方面可能会变得更加一致。 - Timothy Lee Russell

5
我找到了一个更合我口味的答案:
<script language="javascript" type="text/javascript">
document.write("<script language='javascript' type='text/javascript' src='before.js'><\/sc" + "ript>");
document.write("<script defer language='javascript' type='text/javascript'>alert('during');<\/sc" + "ript>");
document.write("<script defer language='javascript' type='text/javascript' src='after.js'><\/sc" + "ript>");
</script>

这将延迟页面加载完成前和完成后的加载。我认为这已经是最好的解释了,希望有人能给出更好的答案。

6
为了保持脚本的一致性顺序,需要先运行内联脚本再运行外部脚本。要达到这个目的,可以将内联代码移到一个外部文件中。 - Eric Bréchemier

1

该演示文稿的第25/26张幻灯片谈论了不同插入脚本方法的特征。它表明,IE是唯一按顺序执行这些脚本的浏览器。其他所有浏览器将按照加载完成的顺序执行它们。即使IE在其中一个或多个脚本具有内联js而不是src时也不会按顺序执行它们。

建议之一是插入新的DOM元素:

var se1 = document.createElement('script');
se1.src = 'a.js';

var se2 = document.createElement('script');
se2.src = 'b.js';

var se3 = document.createElement('script');
se3.src = 'c.js';

var head = document.getElementsByTagName('head')[0]
head.appendChild(se1);
head.appendChild(se2);
head.appendChild(se3);

为了生成第二个脚本部分,您可以使用一个脚本来生成该内容并传递参数:
se2.src = 'generateScript.php?params=' + someParam;

编辑:尽管我引用的文章说,如果每个脚本都有src,大多数浏览器将按顺序执行您的document.write脚本,但我的测试表明,所以虽然我认为上面的方法更好,但您也可以这样做:

<script language="javascript" type="text/javascript">
document.write("<script type='text/javascript' src='before.js'><\/sc" + "ript>");
document.write("<script type='text/javascript' src='during.php?params=" + params + "'><\/sc" + "ript>");
document.write("<script type='text/javascript' src='after.js'><\/sc" + "ript>");
</script>

再次编辑(回应对自己和他人的评论):你已经在页面上生成了脚本。无论你在做什么,都可以将其移动到另一个生成相同代码块的服务器端脚本中。如果你需要在页面上传递参数,则将它们传递给查询字符串中的脚本。

此外,如果像你所建议的那样多次生成内联脚本,则可以使用此相同方法:

<script language="javascript" type="text/javascript">
document.write("<script type='text/javascript' src='before.js'><\/sc" + "ript>");
document.write("<script type='text/javascript' src='during.php?params=" + params1 + "'><\/sc" + "ript>");
document.write("<script type='text/javascript' src='during.php?params=" + params2 + "'><\/sc" + "ript>");
document.write("<script type='text/javascript' src='during.php?params=" + params3 + "'><\/sc" + "ript>");
document.write("<script type='text/javascript' src='after.js'><\/sc" + "ript>");
</script>

然而,看起来你的方法可能是错误的。如果你正在多次生成大量代码,那么你应该用一个单独的JS函数替换它,并使用不同的参数调用它。


“during”是一段非常长的脚本,所以我无法将其简化为一个简单的GET请求... - Omer van Kloeten
@Omer:你已经将它总结为“简单的get请求”了... 你现在是如何生成它并将其内联呈现的?无论你现在正在做什么,你都可以在另一个页面上完成,但只返回js而不是页面的其他部分。 - Prestaul
我提供的演示代码是问题的一个非常简化的复现。我编写的代码是正在生成的代码,文件包括是为了让浏览器缓存它们并减少加载页面所需的时间。 - Omer van Kloeten

1

好的...在...

// During.js
during[fish]();

之后...

// After.js
alert("After");
fish++

HTML

<!-- some html -->
<script language="javascript" type="text/javascript">
document.write("<script language='javascript' type='text/javascript' src='before.js'></sc" + "ript>");
document.write("<script language='javascript' type='text/javascript'>during[" + fish + "] = function(){alert('During!' + fish);}</sc" + "ript>");
document.write("<script language='javascript' type='text/javascript' src='during.js'></sc" + "ript>");
document.write("<script language='javascript' type='text/javascript' src='after.js'></sc" + "ript>");
</script>
<!-- some other html -->
<script language="javascript" type="text/javascript">
document.write("<script language='javascript' type='text/javascript' src='before.js'></sc" + "ript>");
document.write("<script language='javascript' type='text/javascript'>during[" + fish + "] = function(){alert('During!' + fish);}</sc" + "ript>");
document.write("<script language='javascript' type='text/javascript' src='during.js'></sc" + "ript>");
document.write("<script language='javascript' type='text/javascript' src='after.js'></sc" + "ript>");
</script>

我倾向于认同这种感觉的开始,特别是,为什么不能将“during”代码生成到动态创建的js文件中,并插入其中呢?

请注意,动态生成的脚本在第二个document.write函数中的函数内部。
已在FF2,IE7中测试。


1

您可以通过在脚本上定义“onload”(或类似)事件并在事件函数中注入下一个脚本来强制执行顺序。 这并不是一件简单的事情,但是有很多示例可供参考,这里是其中之一。

http://www.phpied.com/javascript-include-ready-onload/

我认为像jQuery或者prototype这样的流行库可以帮助解决这个问题。


0

这样怎么样:

<script>
document.write("<script src='before.js'><\/script>");
</script>

<script >
document.write("<script>alert('during');<\/script>");
</script>

<script>
document.write("<script src='after.js'><\/script>");
</script>

0
提供的代码:
<script language="javascript" type="text/javascript">
document.write("<script language='javascript' type='text/javascript'>function callGeneratedContent() { alert('during'); }<\x2Fscript>");
document.write("<script language='javascript' type='text/javascript' src='before.js'><\x2Fscript>");
document.write("<script language='javascript' type='text/javascript' src='after.js'><\x2Fscript>");
</script>

在 before.js 文件中:
alert("Before");
callGeneratedContent();

在 after.js 中:

alert("After");

你必须先放置生成的代码行,否则 FF 会抱怨因为它在看到函数定义之前执行 before.js。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接