Wie genau funktioniert <script defer = "defer">?

208

Ich habe einige <script>Elemente, und der Code in einigen von ihnen hängt vom Code in anderen <script>Elementen ab. Ich habe gesehen, dass das deferAttribut hier nützlich sein kann, da Codeblöcke bei der Ausführung verschoben werden können.

Um es zu testen, habe ich dies auf Chrome ausgeführt: http://jsfiddle.net/xXZMN/ .

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

Es werden jedoch Warnungen ausgegeben 2 - 1 - 3. Warum alarmiert es nicht 1 - 2 - 3?

pimvdb
quelle
2
Vielleicht lesen Sie diesen Artikel . Und wie immer hat der IE seine eigene Meinung dazu, was etwas bedeutet, und hat beschlossen, zuerst das Skript zu laden, aber die Ausführung zu verzögern, bis der Body geladen ist (normalerweise).
Brad Christie
Vielen Dank, aber die Testseite hat in Chrome ein anderes Ergebnis: websiteoptimization.com/speed/tweak/defer/test . Der Screenshot zeigt, wie ich es erwarten würde, während Chrome den verzögerten Vorgang nur zuerst auszuführen scheint.
Pimvdb
1
Ich denke, Sie werden feststellen, dass die IE-Definition von Defer der Absicht des W3C entspricht, in der DOM Level 1-Spezifikation aufzuschieben.
Mark At Ramp51
41
Wie Alohci bereits in seiner Antwort ausgeführt hat, gilt laut HTML-Standard defer nur bei Angabe src. Dies könnte ein Grund sein, warum Ihr Beispiel in den meisten Browsern nicht wie erwartet funktioniert hat.
Pankrat
2
@Pankrat Wahre Geschichte! Versuchen Sie jsfiddle.net/xXZMN/50 Getestet in Firefox24
m93a

Antworten:

51

AKTUALISIERT: 19.02.2016

Betrachten Sie diese Antwort als veraltet. Weitere Informationen zu neueren Browserversionen finden Sie in anderen Antworten in diesem Beitrag.


Grundsätzlich weist defer den Browser an, "bis es fertig ist" zu warten, bevor das Javascript in diesem Skriptblock ausgeführt wird. Normalerweise geschieht dies, nachdem das DOM vollständig geladen wurde und document.readyState == 4

Das Attribut "Aufschieben" ist spezifisch für den Internet Explorer. In Internet Explorer 8 ist unter Windows 7 das Ergebnis, das ich auf Ihrer JS Fiddle-Testseite sehe, 1 - 2 - 3.

Die Ergebnisse können von Browser zu Browser variieren.

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

Entgegen der weit verbreiteten Meinung folgt der IE häufiger den Standards, als die Leute es zulassen. Tatsächlich ist das Attribut "Aufschieben" in der DOM Level 1-Spezifikation http://www.w3.org/TR/REC-DOM-Level-1/level definiert -one-html.html

Die Definition des W3C für Aufschub: http://www.w3.org/TR/REC-html40/interact/scripts.html#adef-defer :

"Wenn dieses boolesche Attribut festgelegt ist, gibt es dem Benutzeragenten einen Hinweis darauf, dass das Skript keinen Dokumentinhalt generiert (z. B. kein" document.write "in Javascript), sodass der Benutzeragent das Parsen und Rendern fortsetzen kann."

Markieren Sie an der Rampe 51
quelle
8
@ MarkAtRamp51 - Wenn Ihre Antwort veraltet ist, sollten Sie sie bearbeiten, anstatt sich über Kommentare in Kommentaren zu anderen Antworten zu beschweren. Downvotes sind für Antworten, die "nicht nützlich" sind.
Christian Conkle
10
@ChristianConkle Ich schätze die Etikette-Lektion, aber die anderen Antworten hier sind aktuell. Ich ging auf die Tatsache ein, dass zum Zeitpunkt der Fragestellung nicht die falsche Antwort ausgewählt wurde. Vielleicht sollten Sie die Leute überwachen, die falsche Einschätzungen über die Community verbreiten, indem sie Antworten falsch auswählen, anstatt die Leute daran zu erinnern, dass sich die Dinge im Laufe der Zeit ändern und der Kontext wichtig ist. Ich sehe keinen Wert darin, meine Antwort zu entfernen, da historische Informationen ebenfalls wertvoll sind.
Mark At Ramp51
3
"Ich sehe keinen Wert darin, meine Antwort zu entfernen, da historische Informationen ebenfalls wertvoll sind." Wie wäre es in diesem Fall, wenn Sie am Anfang eine Notiz hinzufügen, die darauf hinweist, dass sie nur für Pre-HTML5 gilt, und dann auf das "Richtige" verlinken ( aktuelle) Antwort? Das sollte Ihnen viel Ärger ersparen (als ein Typ zu sprechen, der auch einmal eine "falsche" Antwort akzeptiert hatte und "unter Druck" stand, sie irgendwann zu ändern).
mgibsonbr
3
@Leo sollte es dann nicht markiert werden? Bei der Suche nach "html5 defer script" ist dies das dritte Ergebnis in Google. Diese Antwort liefert dann vielen Benutzern eine veraltete und falsche Definition. (Die aktuelle Definition: "Gibt an, dass der Benutzeragent die Verarbeitung des Skripts verzögern kann. Weitere Informationen finden Sie in der Definition der verzögerten Attribute in HTML 4.0.")
Malavos
2
@ MarkAtRamp51Ich denke, Sie sollten Ihre Antwort aktualisieren. Jeder, der diese Frage und damit Ihre Antwort findet, erkennt ihre historischen Informationen nicht. Es wird für sie so aussehen, als wäre es die Antwort, die heute richtig ist. So funktioniert das Internet. Sie sollten also Ihre Antwort bearbeiten, beachten, dass sie einmal richtig war, und sich auf die richtige Antwort beziehen.
Juuro
167

Einige Ausschnitte aus der HTML5-Spezifikation: http://w3c.github.io/html/semantics-scripting.html#element-attrdef-script-async

Die Attribute defer und async dürfen nicht angegeben werden, wenn das Attribut src nicht vorhanden ist.


Es gibt drei mögliche Modi, die mit diesen Attributen ausgewählt werden können [asynchron und verzögert]. Wenn das asynchrone Attribut vorhanden ist, wird das Skript asynchron ausgeführt, sobald es verfügbar ist. Wenn das asynchrone Attribut nicht vorhanden ist, das verzögerte Attribut jedoch vorhanden ist, wird das Skript ausgeführt, wenn die Analyse der Seite abgeschlossen ist. Wenn keines der Attribute vorhanden ist, wird das Skript sofort abgerufen und ausgeführt, bevor der Benutzeragent die Seite weiter analysiert.


Die genauen Verarbeitungsdetails für diese Attribute sind aus meist historischen Gründen nicht trivial und betreffen eine Reihe von Aspekten von HTML. Die Implementierungsanforderungen sind daher notwendigerweise über die gesamte Spezifikation verteilt. Die folgenden Algorithmen (in diesem Abschnitt) beschreiben den Kern dieser Verarbeitung. Diese Algorithmen verweisen jedoch auf die Parsing-Regeln für Start- und End-Tags von Skripten in HTML, in fremden Inhalten und in XML auf die Regeln für document.write () Methode, die Handhabung von Skripten usw.


Wenn das Element ein src-Attribut und das Element ein defer-Attribut hat und das Element als "Parser-Insert" gekennzeichnet wurde und das Element kein asynchrones Attribut hat:

Das Element muss am Ende der Liste der Skripte hinzugefügt werden, die ausgeführt werden, wenn das Parsen des Dokuments abgeschlossen ist, das dem Dokument des Parsers zugeordnet ist, der das Element erstellt hat.

Alohci
quelle
37
Vielleicht hindert meine Antwort hier die Leute daran, meine Antwort aufgrund Ihres nicht hilfreichen Kommentars abzustimmen. Die akzeptierte Antwort ist nicht falsch, die Antwort ist anders, da die HTML5-Spezifikation Anfang 2011 für die Mainstream-Webbrowser weniger relevant war als derzeit. Diese Antwort könnte in Zukunft besser sein, aber die akzeptierte Antwort ist in keiner Weise FALSCH .
Markieren Sie an der Rampe 51
3
Es ist zwar nützlich zu wissen, was die Spezifikation sagt, aber es stellt sich heraus, dass einige Browser wie IE <9 deferschlecht implementiert sind . Wenn Sie verwenden defer, können Sie sich nicht darauf verlassen, dass die Skriptdateien in einigen Browsern in der richtigen Reihenfolge ausgeführt werden.
Flimm
2
@Flimm Nicht nur IE, es scheint, dass die Ausführungsreihenfolge auch in Firefox nicht garantiert ist .
Franklin Yu
Das erste Zitat ist nicht mehr gültig, oder? Jetzt kann ich folgendes lesen: "Das Attribut darf nicht angegeben werden, wenn das src-Attribut nicht vorhanden ist oder wenn das Skript kein klassisches Skript ist." Und ein klassisches Skript ist auch eines ohne src = "".
Félix Sanz
158

Die wirkliche Antwort lautet: Weil Sie nicht aufschieben können.

Im Konzept unterscheiden sich Defer und Async wie folgt:

Mit async kann das Skript im Hintergrund heruntergeladen werden, ohne es zu blockieren. Sobald der Download abgeschlossen ist, wird das Rendern blockiert und das Skript ausgeführt. Das Rendern wird fortgesetzt, wenn das Skript ausgeführt wurde.

defer macht dasselbe, mit Ausnahme der Ansprüche, die garantieren, dass Skripte in der Reihenfolge ausgeführt werden, in der sie auf der Seite angegeben wurden, und dass sie ausgeführt werden, nachdem das Parsen des Dokuments abgeschlossen ist. Einige Skripte werden möglicherweise heruntergeladen, warten dann auf Skripte, die später heruntergeladen wurden, aber vor ihnen angezeigt wurden.

Leider variiert die Definition von Defer aufgrund eines wirklich standardmäßigen Katzenkampfes von Spezifikation zu Spezifikation, und selbst in den neuesten Spezifikationen bietet dies keine nützliche Garantie. Wie die Antworten hier und in diesem Problem zeigen, implementieren Browser die Verzögerung anders:

  • In bestimmten Situationen haben einige Browser einen Fehler, der dazu führt, dass deferSkripte nicht mehr in der richtigen Reihenfolge ausgeführt werden.
  • Einige Browser verzögern das DOMContentLoadedEreignis, bis die deferSkripte geladen wurden, andere nicht.
  • Einige Browser gehorchen deferauf <script>Elemente mit Inline - Code und ohne srcAttribut, und einige ignorieren.

Glücklicherweise gibt die Spezifikation zumindest an, dass asynchrone Überschreibungen verzögert werden. So können Sie alle Skripte als asynchron behandeln und eine breite Palette an Browserunterstützung erhalten, wie folgt:

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

98% der weltweit verwendeten Browser und 99% in den USA vermeiden mit diesem Ansatz das Blockieren.

(Wenn Sie warten müssen, bis das Dokument analysiert wurde, hören Sie sich das Ereignis an DOMContentLoadedoder verwenden Sie die praktische .ready()Funktion von jQuery . Sie sollten dies trotzdem tun, um auf Browser zurückzugreifen, die überhaupt nicht implementiert sind defer.)

Chris Moschini
quelle
13
Danke, deine Antwort war für mich am hilfreichsten!
Markus
5
Ich glaube das ist falsch. Der Vorteil von Defer ist, dass es erst ausgeführt wird, wenn das Parsen der Seite abgeschlossen ist. Diese Seite hat ein gutes Bild, um den Unterschied zwischen asynchron und verzögert
tinkerr
1
@tinkerr Im Konzept sind Sie richtig; in der Praxis stellt sich dies nicht als wahr heraus. Da die Sequenzgarantie nicht konsistent implementiert ist, ist sie nicht universell und daher keine Garantie. Wenn Sie etwas implementieren, ist Ihnen die Ausführung wichtig. Die Absicht des Designs ist niedlich, aber nicht besonders hilfreich.
Chris Moschini
Ich wollte nur darauf hinweisen, dass Opera das deferAttribut seit Version 15 unterstützt , die am 2. Juni 2013 veröffentlicht wurde .
1
@VikasBansal Für ältere Browser, die Async nicht unterstützen - nämlich ältere IE.
Chris Moschini
13

deferkann nur in <script>Tags für die Aufnahme externer Skripte verwendet werden. Daher wird empfohlen, <script>in den <head>-tags im -section zu verwenden.

Rajesh Paul
quelle
8

Das Attribut as defer funktioniert nur mit dem Skript-Tag src. Es wurde eine Möglichkeit gefunden, die Verzögerung für Inline-Skripte nachzuahmen. Verwenden Sie das DOMContentLoaded-Ereignis.

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

Dies liegt daran, dass das Ereignis DOMContentLoaded ausgelöst wird, nachdem zurückgestellte zugeordnete Skripte vollständig geladen wurden.

Bijoy Anupam
quelle
6

Das Attribut defer gilt nur für externe Skripte (sollte nur verwendet werden, wenn das Attribut src vorhanden ist).

Soumitra
quelle
4

Schauen Sie sich diesen hervorragenden Artikel an. Tauchen Sie tief in das trübe Wasser des Ladens von Skripten ein, das der Google-Entwickler Jake Archibald 2013 geschrieben hat.

Zitieren des relevanten Abschnitts aus diesem Artikel:

Verschieben

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

Spezifikation sagt : Zusammen herunterladen, in der Reihenfolge kurz vor DOMContentLoaded ausführen. Ignorieren Sie "Aufschieben" bei Skripten ohne "src".

IE <10 sagt : Ich könnte 2.js in der Mitte der Ausführung von 1.js ausführen. Macht das nicht Spaß?

Die Browser in Rot sagen : Ich habe keine Ahnung, was dieses "Aufschieben" ist, ich werde die Skripte laden, als ob es nicht da wäre.

Andere Browser sagen : Ok, aber ich kann "Aufschieben" von Skripten ohne "src" nicht ignorieren.

(Ich werde hinzufügen, dass frühe Versionen von Firefox DOMContentLoaded auslösen, bevor die deferSkripte laut diesem Kommentar ausgeführt werden .)

Moderne Browser scheinen dies asyncordnungsgemäß zu unterstützen , aber Sie müssen mit Skripten einverstanden sein, die nicht in der richtigen Reihenfolge und möglicherweise vor DOMContentLoaded ausgeführt werden.

Flimm
quelle
1

Dieses boolesche Attribut gibt einem Browser an, dass das Skript ausgeführt werden soll, nachdem das Dokument analysiert wurde. Da diese Funktion noch nicht von allen anderen gängigen Browsern implementiert wurde, sollten Autoren nicht davon ausgehen, dass die Ausführung des Skripts tatsächlich verzögert wird. Rufen Sie niemals document.write () aus einem verzögerten Skript auf (seit Gecko 1.9.2 wird das Dokument dadurch weggeblasen). Das Attribut defer sollte nicht für Skripte verwendet werden, die das Attribut src nicht haben. Seit Gecko 1.9.2 wird das Attribut defer in Skripten ignoriert, die das Attribut src nicht haben. In Gecko 1.9.1 werden jedoch auch Inline-Skripte zurückgestellt, wenn das Attribut defer festgelegt ist.

Aufschieben funktioniert mit Chrom, Firefox, dh> 7 und Safari

Ref: https://developer.mozilla.org/en-US/docs/HTML/Element/script

S-Sharma
quelle
0

Das Defer-Attribut ist ein boolesches Attribut.

Wenn vorhanden, wird angegeben, dass das Skript ausgeführt wird, wenn die Analyse der Seite abgeschlossen ist.

Hinweis: Das Attribut defer gilt nur für externe Skripte (sollte nur verwendet werden, wenn das Attribut src vorhanden ist).

Hinweis: Es gibt verschiedene Möglichkeiten, wie ein externes Skript ausgeführt werden kann:

Wenn Async vorhanden ist: Das Skript wird asynchron mit dem Rest der Seite ausgeführt (das Skript wird ausgeführt, während die Seite das Parsen fortsetzt). Wenn Async nicht vorhanden ist und Defer vorhanden ist: Das Skript wird ausgeführt, wenn die Seite das Parsen beendet hat. If Es ist weder asynchron noch verzögert vorhanden: Das Skript wird sofort abgerufen und ausgeführt, bevor der Browser die Seite weiter analysiert

srikanth_k
quelle