web-dev-qa-db-fra.com

JavaScript document.write Inline Script Execution Order

J'ai le script suivant, où le premier et le troisième document.writeline sont statiques et le second est généré :

<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 et Chrome affichera avant, pendant et après, tandis qu'Internet Explorer affiche d'abord pendant et seulement alors il affiche avant et après.

Je suis tombé sur n article qui dit que je ne suis pas le premier à rencontrer cela, mais cela ne me fait guère me sentir mieux.

Quelqu'un sait-il comment je peux définir l'ordre pour qu'il soit déterministe dans tous les navigateurs, ou pirater IE pour fonctionner comme tous les autres navigateurs sains?)

Mises en garde : L'extrait de code est une repro très simple. Il est généré sur le serveur et le deuxième script est la seule chose qui change. C'est un long script et la raison pour laquelle il y a deux scripts avant et après c'est pour que le navigateur les mette en cache et que la partie dynamique du code soit aussi petite que possible. Il peut également apparaître plusieurs fois sur la même page avec un code généré différent.

36
Omer van Kloeten

J'ai trouvé une réponse plus à mon goût:

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

Cela retardera le chargement des deux pendant et après jusqu'à ce que la page ait fini de se charger.

Je pense que c'est aussi bon que possible. J'espère que quelqu'un pourra donner une meilleure réponse.

5
Omer van Kloeten

Non, c'est le comportement d'Internet Explorer.

Si vous attachez des scripts de manière dynamique, IE, Firefox et Chrome téléchargeront tous les scripts de manière asynchrone.

Firefox et Chrome attendra que toutes les demandes asynchrones reviennent, puis exécutera les scripts dans l'ordre dans lequel ils sont attachés dans le DOM mais IE exécute le scripts dans l'ordre où ils sont retournés sur le fil.

Étant donné que l'alerte prend moins de temps à "récupérer" qu'un fichier javascript externe, cela explique probablement le comportement que vous voyez.

Extrait de Kristoffer Henriksson publication sur le sujet du chargement de script asynchrone :

Dans ce scénario IE et Firefox téléchargeront les deux scripts mais Internet Explorer les exécutera également dans l'ordre dans lequel ils ont terminé le téléchargement tandis que Firefox les télécharge de manière asynchrone mais les exécute toujours dans l'ordre dans lequel ils sont attachés dans le DOM.

Dans Internet Explorer, cela signifie que vos scripts ne peuvent pas avoir de dépendances les uns des autres car l'ordre d'exécution varie en fonction du trafic réseau, des caches, etc.

Pensez à utiliser un chargeur Javascript. Il vous permettra de spécifier les dépendances de script et l'ordre d'exécution tout en chargeant vos scripts de manière asynchrone pour la vitesse ainsi que de lisser certaines des différences de navigateur.

Voici un assez bon aperçu de certains d'entre eux: JavaScript essentiel: les cinq premiers chargeurs de script .

J'ai utilisé à la fois RequireJS et LabJS. À mon avis, LabJS est un peu moins opiniâtre.

24
Timothy Lee Russell

D'accord ... pendant ...

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

après...

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

Je suis toutefois enclin à être d'accord sur la façon dont cela commence à sentir. En particulier, pourquoi ne pourriez-vous pas coder le "pendant" dans un fichier js créé dynamiquement et l'insérer?

Notez que le script généré dynamiquement va à l'intérieur la fonction dans le second document.write.
Testé en FF2, IE7

1
Dustman

Diapositives 25/26 de cette présentation parler des caractéristiques des différentes méthodes d'insertion de scripts. Il suggère que IE est le seul navigateur qui exécutera ces scripts dans l'ordre. Tous les autres navigateurs les exécuteront dans l'ordre de fin de chargement. Even IE ne les exécutera pas dans l'ordre si un ou plusieurs ont des js en ligne au lieu d'un src.

L'une des méthodes suggérées consiste à insérer un nouvel élément 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);

Pour générer la deuxième section de script, vous pouvez utiliser un script pour générer ce contenu et transmettre les paramètres:

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

EDIT: Malgré ce que dit l'article que j'ai cité, mes tests suggèrent que la plupart des navigateurs exécuteront vos scripts document.write dans l'ordre s'ils ont chacun un src, donc même si je pense que la méthode ci-dessus est préférée, vous pouvez également le faire :

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

MODIFIER ENCORE (réponse aux commentaires à moi-même et aux autres): Vous générez déjà le script sur votre page. Tout ce que vous faites peut être déplacé vers un autre script côté serveur qui génère le même bloc de code. Si vous avez besoin de paramètres sur votre page, passez-les au script dans la chaîne de requête.

En outre, vous pouvez utiliser cette même méthode si, comme vous le suggérez, vous générez plusieurs fois le script en ligne:

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

Cependant, cela commence à ressembler à une approche erronée. Si vous générez un grand bloc de code plusieurs fois, vous devriez probablement le remplacer par une seule fonction js et l'appeler avec différents paramètres à la place ...

1
Prestaul

Vous pouvez forcer l'exécution sécuentielle en définissant des événements "onload" (ou similaires) sur les scripts et injecter le suivant dans la fonction d'événement. Ce n'est pas anodin, mais il existe de nombreux exemples, en voici un.

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

Je pense que les bibliothèques populaires comme jQuery ou prototype peuvent vous aider.

1
Victor

Code à fournir:

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

Dans before.js:

alert("Before");
callGeneratedContent();

Dans after.js:

alert("After");

Vous devez d'abord mettre la ligne générée, sinon FF se plaindra car elle s'exécute avant.js avant de voir la définition de la fonction.

0
PhiLho

Que diriez-vous de cela:

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