Question

I have the following script, where the first and third document.writeline are static and the second is generated:

<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 and Chrome will display before, during and after, while Internet Explorer first shows during and only then does it show before and after.

I've come across an article that states that I'm not the first to encounter this, but that hardly makes me feel any better.

Does anyone know how I can set the order to be deterministic in all browsers, or hack IE to work like all the other, sane browsers do?

Caveats: The code snippet is a very simple repro. It is generated on the server and the second script is the only thing that changes. It's a long script and the reason there are two scripts before and after it are so that the browser will cache them and the dynamic part of the code will be as small as possible. It may also appears many times in the same page with different generated code.

Was it helpful?

Solution 2

I've found an answer more to my liking:

<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>

This will defer the loading of both during and after until the page has finished loading.

I think this is as good as I can get. Hopefully, someone will be able to give a better answer.

OTHER TIPS

No, this is the behavior of Internet Explorer.

If you attach scripts dynamically, IE, Firefox, and Chrome will all download the scripts in an asynchronous manner.

Firefox and Chrome will wait till all of the async requests return and then will execute the scripts in the order that they are attached in the DOM but IE executes the scripts in the order that they are returned over the wire.

Since the alert takes less time to "retrieve" than an external javascript file, that likely explains the behavior that you are seeing.

From Kristoffer Henriksson's post on the subject of asynchronous script loading:

In this scenario IE and Firefox will download both scripts but Internet Explorer will also execute them in the order they finish downloading in whereas Firefox downloads them asynchronously but still executes them in the order they are attached in the DOM.

In Internet Explorer this means your scripts cannot have dependancies on one another as the execution order will vary depending on network traffic, caches, etc.

Consider using a Javascript loader. It will let you specify script dependencies and order of execution while also loading your scripts asynchronously for speed as well as smoothing out some of the browser differences.

This is a pretty good overview of a few of them: Essential JavaScript: the top five script loaders.

I've used both RequireJS and LabJS. In my opinion, LabJS is a little less opinionated.

Slides 25/26 of this presentation talk about the characteristics of different methods for inserting scripts. It suggests that IE is the only browser that will execute those scripts in order. All other browsers will execute them in the order that they finish loading. Even IE won't execute them in order if one or more have inline js instead of a src.

One of the methods suggested is to insert a new DOM element:

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);

To make the second script section generated you could use a script to generate that content and pass the parameters:

se2.src = 'generateScript.php?params=' + someParam;

EDIT: In spite of what the article I sited says, my testing suggests that most browsers will execute your document.write scripts in order if they each have a src, so while I think the method above is preferred, you could do this as well:

<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>

EDIT AGAIN (response to comments to myself and others): You are already generating the script on your page. Whatever you are doing can be moved to another server-side script that generates the same block of code. If you need parameters on your page then pass them to the script in the query string.

Also, you can use this same method if, as you suggest, you are generating the inline script multiple times:

<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>

However, this is starting to look as though you are approaching this the wrong way. If you are generating a large block of code multiple times then you should probably be replacing it with a single js function and calling it with different params instead...

Okay...during...

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

after...

// 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>

I'm inclined to agree about the way this is starting to smell, though. In particular, why couldn't you codegen the "during" into a dynamically created js file, and insert that?

Note that the dynamically generated script goes inside the function in the second document.write.
Tested in FF2, IE7

You can force secuential execution by defining "onload" (or similar) events on the scripts and inject the next one in the event function. It is not trivial, but there are plenty of examples out there, here is one.

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

I think popular libraries like jQuery or prototype can help with this.

Code to provide:

<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>

In before.js:

alert("Before");
callGeneratedContent();

In after.js:

alert("After");

You have to put the generated line first, otherwise FF will complain because it executes before.js before seeing the function definition.

How about that:

<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>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top