Comment fonctionne exactement <script defer = “defer”>?

208

J'ai quelques <script>éléments, et le code de certains d'entre eux dépend du code d'autres <script>éléments. J'ai vu que l' deferattribut peut être utile ici car il permet de retarder l'exécution des blocs de code.

Pour le tester, j'ai exécuté ceci sur Chrome: http://jsfiddle.net/xXZMN/ .

<script defer="defer">alert(2);</script>
<script>alert(1)</script>
<script defer="defer">alert(3);</script>

Cependant, il alerte 2 - 1 - 3. Pourquoi ne l'alerte- 1 - 2 - 3t- il pas ?

pimvdb
la source
2
Consultez peut-être cet article . Et, comme toujours, IE a son propre point de vue sur ce que quelque chose signifie et a décidé de charger le script en premier mais de retarder l'exécution jusqu'à ce que le corps soit chargé (généralement).
Brad Christie
Merci, mais la page de test a un résultat différent sur Chrome: websiteoptimization.com/speed/tweak/defer/test . La capture d'écran montre à quoi je m'attendrais, tandis que Chrome semble simplement exécuter le différé en premier.
pimvdb
1
Je pense que vous trouverez la définition d'IE de différer, qui correspond à l'intention du W3C de différer dans la spécification DOM niveau 1.
Mark At Ramp51
41
Comme Alohci l'a déjà souligné dans sa réponse, selon la norme HTML defer n'est valable que lors de la spécification src. Cela pourrait être une raison pour laquelle votre exemple n'a pas fonctionné comme prévu dans la plupart des navigateurs.
Pankrat
2
@Pankrat True story! Essayez jsfiddle.net/xXZMN/50 Testé dans Firefox24
m93a

Réponses:

51

MISE À JOUR: 19/02/2016

Considérez cette réponse comme obsolète. Référez-vous à d'autres réponses sur ce post pour des informations pertinentes pour la nouvelle version du navigateur.


Fondamentalement, différer indique au navigateur d'attendre "jusqu'à ce qu'il soit prêt" avant d'exécuter le javascript dans ce bloc de script. Habituellement, c'est après que le DOM a fini de charger et de document.readyState == 4

L'attribut defer est spécifique à Internet Explorer. Dans Internet Explorer 8, sur Windows 7, le résultat que je vois dans votre page de test JS Fiddle est 1 - 2 - 3.

Les résultats peuvent varier d'un navigateur à l'autre.

http://msdn.microsoft.com/en-us/library/ms533719(v=vs.85).aspx

Contrairement à la croyance populaire, IE suit les normes plus souvent que les gens ne le disent, en réalité l'attribut "defer" est défini dans la spécification DOM niveau 1 http://www.w3.org/TR/REC-DOM-Level-1/level -one-html.html

La définition du W3C de différer: http://www.w3.org/TR/REC-html40/interact/scripts.html#adef-defer :

"Lorsqu'il est défini, cet attribut booléen indique à l'agent utilisateur que le script ne va pas générer de contenu de document (par exemple, pas de" document.write "en javascript) et ainsi, l'agent utilisateur peut continuer l'analyse et le rendu."

Mark At Ramp51
la source
8
@ MarkAtRamp51 - Si votre réponse est obsolète, vous devez la modifier au lieu de vous plaindre des votes négatifs dans les commentaires sur les autres réponses. Les downvotes concernent les réponses qui ne sont "pas utiles".
Christian Conkle
10
@ChristianConkle J'apprécie la leçon d'étiquette, mais les autres réponses ici sont à jour. Je parlais du fait que la mauvaise réponse n'avait pas été sélectionnée au moment où la question a été posée. Peut-être devriez-vous surveiller les gens qui répandent de fausses évaluations sur la communauté en sélectionnant incorrectement les réponses, au lieu de chercher à rappeler aux gens que les choses changent avec le temps et que le contexte est important. Je ne vois aucune valeur à retirer ma réponse, car les informations historiques sont également précieuses.
Mark At Ramp51
3
"Je ne vois pas de valeur à supprimer ma réponse, car les informations historiques sont également utiles" Dans ce cas, que diriez-vous d'ajouter une note au début en soulignant qu'elle ne s'applique qu'au pré-HTML5, puis de créer un lien vers le "bon" ( à jour) réponse? Cela devrait vous éviter bien des ennuis (parler comme un gars qui a également accepté une "mauvaise" réponse et qui a été "poussé par des pairs" pour éventuellement la changer).
mgibsonbr
3
@Leo ne devrait-il pas être signalé alors? En recherchant "html5 defer script", c'est le troisième résultat dans google. Cette réponse fournit alors à beaucoup d'utilisateurs une définition obsolète et incorrecte. (La définition actuelle: "Indique que l'agent utilisateur peut différer le traitement du script. Voir la définition de l'attribut defer en HTML 4.0.").
Malavos
2
@ MarkAtRamp51Je pense que vous devriez mettre à jour votre réponse. Quiconque trouve cette question et donc votre réponse ne reconnaîtra pas ses informations historiques. Il leur semblera que c'est la réponse qui est correcte aujourd'hui. Voilà comment fonctionne Internet. Vous devez donc modifier votre réponse, noter qu'elle était correcte une fois et vous référer à la bonne réponse.
Juuro
167

Quelques extraits de la spécification HTML5: http://w3c.github.io/html/semantics-scripting.html#element-attrdef-script-async

Les attributs defer et async ne doivent pas être spécifiés si l'attribut src n'est pas présent.


Il existe trois modes possibles qui peuvent être sélectionnés à l'aide de ces attributs [async et différé]. Si l'attribut async est présent, le script sera exécuté de manière asynchrone, dès qu'il sera disponible. Si l'attribut async n'est pas présent mais que l'attribut defer est présent, le script est exécuté lorsque la page a terminé l'analyse. Si aucun attribut n'est présent, le script est récupéré et exécuté immédiatement, avant que l'agent utilisateur continue d'analyser la page.


Les détails exacts du traitement de ces attributs sont, pour des raisons principalement historiques, quelque peu non triviaux, impliquant un certain nombre d'aspects du HTML. Les exigences de mise en œuvre sont donc nécessairement dispersées dans la spécification. Les algorithmes ci-dessous (dans cette section) décrivent le cœur de ce traitement, mais ces algorithmes font référence et sont référencés par les règles d'analyse des balises de début et de fin de script en HTML, en contenu étranger et en XML, les règles du document.write () méthode, le traitement des scripts, etc.


Si l'élément a un attribut src, et l'élément a un attribut defer, et l'élément a été marqué comme "inséré par l'analyseur", et l'élément n'a pas d'attribut async:

L'élément doit être ajouté à la fin de la liste des scripts qui s'exécuteront lorsque le document aura terminé l'analyse associée au document de l'analyseur qui a créé l'élément.

Alohci
la source
37
Peut-être que ma réponse ici empêchera les gens de voter contre ma réponse, en raison de votre commentaire inutile. La réponse acceptée n'est pas fausse, la réponse est différente, car au début de 2011, la spécification HTML5 était moins pertinente pour les navigateurs Web traditionnels qu'elle ne l'est actuellement. Cette réponse pourrait être mieux à l'avenir, mais la réponse acceptée n'est FAUX par aucune norme.
Mark At Ramp51
3
Bien qu'il soit utile de savoir ce que dit la spécification, il s'avère que certains navigateurs comme IE <9 implémentent defermal . Si vous utilisez defer, vous ne pouvez pas vous fier aux fichiers de script exécutés dans l'ordre dans certains navigateurs.
Flimm
2
@Flimm Pas seulement IE, il semble que l'ordre d'exécution ne soit pas non plus garanti dans Firefox .
Franklin Yu
Le premier devis n'est plus valable non? Maintenant je peux lire ceci: "L'attribut ne doit pas être spécifié si l'attribut src n'est pas présent, ou si le script n'est pas un script classique.". Et un script classique est aussi sans src = "".
Félix Sanz
158

La vraie réponse est: parce que vous ne pouvez pas faire confiance à différer.

Dans le concept, différer et asynchroniser diffèrent comme suit:

async permet de télécharger le script en arrière-plan sans le bloquer. Ensuite, au moment où il termine le téléchargement, le rendu est bloqué et ce script s'exécute. Le rendu reprend une fois le script exécuté.

reporter fait la même chose, sauf qu'il prétend garantir que les scripts s'exécutent dans l'ordre où ils ont été spécifiés sur la page, et qu'ils seront exécutés une fois l'analyse du document terminée. Ainsi, certains scripts peuvent terminer le téléchargement puis s'asseoir et attendre les scripts téléchargés plus tard mais apparus devant eux.

Malheureusement, en raison de ce qui est vraiment un combat de chats standard, la définition de defer varie selon les spécifications, et même dans les spécifications les plus récentes, n'offre pas de garantie utile. Comme les réponses ici et ce problème le démontrent, les navigateurs implémentent différer différemment:

  • Dans certaines situations, certains navigateurs ont un bogue qui provoque le deferdysfonctionnement des scripts.
  • Certains navigateurs retardent l' DOMContentLoadedévénement jusqu'à ce que les deferscripts soient chargés, et d'autres non.
  • Certains navigateurs obéissent deferà des <script>éléments avec du code en ligne et sans srcattribut, et certains l'ignorent.

Heureusement, la spécification spécifie au moins que les substitutions asynchrones reportent. Ainsi, vous pouvez traiter tous les scripts comme asynchrones et obtenir une large gamme de support de navigateur comme ceci:

<script defer async src="..."></script>

98% des navigateurs utilisés dans le monde et 99% aux États-Unis éviteront le blocage avec cette approche.

(Si vous devez attendre la fin de l'analyse du document, écoutez l'événement d' DOMContentLoadedévénement ou utilisez la .ready()fonction pratique de jQuery . Vous voudrez quand même le faire pour vous rabattre gracieusement sur les navigateurs qui ne l'implémentent pas deferdu tout.)

Chris Moschini
la source
13
Merci, votre réponse m'a été la plus utile!
Markus
5
Je pense que c'est incorrect. L'avantage de différer est qu'il ne s'exécute pas tant que l'analyse de la page n'est pas terminée. Cette page a un bon visuel pour expliquer la différence entre async et defer
tinkerr
1
@tinkerr Dans le concept, vous avez raison; en pratique, cela ne s'avère pas vrai. Parce qu'elle n'est pas implémentée de manière cohérente, la garantie de séquence n'est pas universelle et donc pas une garantie. Lorsque vous implémentez quelque chose, vous vous souciez de l'exécution. L'intention de la conception est mignonne, mais pas particulièrement utile.
Chris Moschini
Je voulais juste souligner que Opera prend en charge l' deferattribut depuis la version 15 , qui a été publiée le 2 juin 2013 .
1
@VikasBansal Pour les navigateurs plus anciens qui ne prennent pas en charge l'async - à savoir IE plus ancien.
Chris Moschini
13

deferne peut être utilisé que dans la <script>balise pour l' inclusion de script externe . Par conséquent, il est conseillé de l'utiliser dans les <script>balises-de la section <head>.

Rajesh Paul
la source
8

Comme l'attribut defer ne fonctionne qu'avec la balise scripts avec src. Trouvé un moyen d'imiter le report des scripts en ligne. Utilisez l'événement DOMContentLoaded.

<script defer src="external-script.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
    // Your inline scripts which uses methods from external-scripts.
});
</script>

En effet, l'événement DOMContentLoaded se déclenche après le chargement complet des scripts attribués différés.

Bijoy Anupam
la source
6

L'attribut defer est uniquement pour les scripts externes (ne doit être utilisé que si l'attribut src est présent).

Soumitra
la source
4

Jetez un œil à cet excellent article Plongez dans les eaux troubles du chargement de scripts par le développeur Google Jake Archibald écrit en 2013.

Citant la section pertinente de cet article:

Reporter

<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>

Spec dit : Téléchargez ensemble, exécutez dans l'ordre juste avant DOMContentLoaded. Ignorez «reporter» sur les scripts sans «src».

IE <10 dit : je pourrais exécuter 2.js à mi-chemin de l'exécution de 1.js. N'est-ce pas amusant ??

Les navigateurs en rouge disent : Je n'ai aucune idée de ce qu'est ce "report", je vais charger les scripts comme s'ils n'étaient pas là.

D'autres navigateurs disent : Ok, mais je ne pourrais pas ignorer «différer» les scripts sans «src».

(J'ajouterai que les premières versions de Firefox déclenchent DOMContentLoaded avant la deferfin des scripts, selon ce commentaire .)

Les navigateurs modernes semblent prendre en charge asynccorrectement, mais vous devez être d'accord avec les scripts en panne et éventuellement avant DOMContentLoaded.

Flimm
la source
1

Cet attribut booléen est défini pour indiquer à un navigateur que le script est destiné à être exécuté après l'analyse du document. Étant donné que cette fonctionnalité n'a pas encore été implémentée par tous les autres principaux navigateurs, les auteurs ne doivent pas supposer que l'exécution du script sera réellement différée. N'appelez jamais document.write () à partir d'un script différé (depuis Gecko 1.9.2, cela détruirait le document). L'attribut defer ne doit pas être utilisé sur des scripts qui n'ont pas l'attribut src. Depuis Gecko 1.9.2, l'attribut defer est ignoré sur les scripts qui n'ont pas l'attribut src. Cependant, dans Gecko 1.9.1, même les scripts en ligne sont différés si l'attribut defer est défini.

defer fonctionne avec Chrome, Firefox, c'est-à-dire> 7 et Safari

réf: https://developer.mozilla.org/en-US/docs/HTML/Element/script

s-sharma
la source
0

L'attribut defer est un attribut booléen.

Lorsqu'il est présent, il spécifie que le script est exécuté lorsque la page a terminé l'analyse.

Remarque: L'attribut defer est uniquement pour les scripts externes (ne doit être utilisé que si l'attribut src est présent).

Remarque: Il existe plusieurs façons d'exécuter un script externe:

Si async est présent: le script est exécuté de manière asynchrone avec le reste de la page (le script sera exécuté pendant que la page continue l'analyse) Si async n'est pas présent et différé est présent: Le script est exécuté lorsque la page a terminé l'analyse Si ni async ni report n'est présent: le script est récupéré et exécuté immédiatement, avant que le navigateur ne continue d'analyser la page

srikanth_k
la source