Javascript defer und async – einbinden und ausführen

Javascript mit script-Tag einbinden

Das script-Tag darf sowohl im head-Tag als auch an beliebiger Stelle des HTML-Dokuments stehen, aber Javascript wird heute bevorzugt ans Ende der HTML-Seite gesetzt.

Externe Scripte werden mit async asynchron – parallel zu anderen Ressourcen – geladen. defer verspricht dem Browser, dass die Webseite nicht durch Anweisungen wie document.write geändert wird.

23-02-02 SITEMAP CSS HTML JS Basis JS Web Tutorial SVG

Ablauf

Der Browser interpretiert das HTML-Dokument Zeile für Zeile. Steht das script-Tag im head-Tag der Seite, wird der Browser das Javascript an Ort und Stelle ausführen.

Die meisten Funktionen können allerdings erst ausgeführt werden, wenn bestimmte Elemente der Webseite geladen sind. Der Browser muss die angesprochenen Elemente bereits geladen haben, sonst entsteht ein Javascript-Fehler.

	
<html lang="de">
<head>
   <title>Wohin mit dem script-Tag?</title>
   <link media="screen" href="style.css">
   <script>
      let header = querySelector ('header'); <-- Fehlermeldung
   </script>                                        |
</head>                                             |
<body>                                              |
   <header>           existiert erst hier <---------+
       …
   </header>
</body>
</html>

Die Ausführung des Scripts muss mit onload oder addEventListener unterbrochen und zurückgestellt werden, bis das Dom geladen ist und durchquert werden kann.

<head>
   <script>
      window.addEventListener ('load', function () {
         let header = querySelector ('header');
      });
   </script>
</head>
<body>
   <header>
       …
   </header>
</body>

Die Logik für das Anhalten des Scripts und das Registrieren des Events, bei dem das Script fortgesetzt werden soll, ist kompliziert. In der Vergangenheit musste das Javascript darum zu wilden Konstruktionen greifen.

Script am Ende der Seite

Es ist einfacher, das Script am Ende der HTML-Seite vor dem schließenden body-Tag unterzubringen. Die generelle Regel lautet:

CSS-Dateien im head, Javascript am Ende der Webseite einbinden.

<html lang="de">
<head>
   …
</head>
<body>
   <div id="header"> … </div>
   
   <script src="/jquery.min.js"></script>
   <script>
      alert ("Hallo World!");
      document.getElementById("header");
   </script>
</body>
</html>

Wenn es gute Gründe gibt, Javascript bereits im head zu laden, rangiert das Script nach den CSS-Dateien.

Nur ein Bild
»Eliminate render-blocking JavaScript and CSS in above-the-fold» – das beschleunigt die Anzeige der Seite im Browser, aber ist ein Spagat zwischen Programmlogik und der Platzierung des Scripts.

Script ohne async / defer laden

Auch wenn das Script am Ende der Seite geladen wird und selbst wenn die Seite bereits im Browser gerendert ist, kann der Benutzer vor dem Laden und Interpretieren des Scripts nicht auf Links zugreifen und nicht scrollen.

Script ohne defer und ohne async laden

Script asynchron laden

Mit async lädt der Browser das Script parallel zu anderen Ressourcen und beginnt derweil mit dem Aufbau der Seite. Sobald das Script geladen ist, entsteht eine Pause, weil der Browser das Script erst interpretieren muss.

Vorzugsweise sollten externe Scripts asynchron geladen werden. Für Third Party-Scripte ist async ein Muss – wenn deren Server gerade in die Knie geht, zieht das Script die eigene Seite in den Abgrund. Das asynchrone Laden von externen Scripten kürzt die Ladezeit und das Script kann sofort ausgeführt werden.

async kann aber auch ein Dilemma sein, denn es ist nicht vorhersehbar, wann das Script tatsächlich geladen und ausführbar ist.

Script asynchron mit async laden

Laden mit defer verzögern

Das defer-Attribut im script-Tag verspricht dem Browser, dass die Webseite nicht durch Anweisungen wie document.write (was sowieso unerwünscht ist) geändert wird. Der Browser verschiebt das Laden und Ausführen des Scripts, bis alle anderen Komponenten geladen und die Seite geparst ist.

Es ist nicht einfach vorauszusehen, wann das Script tatsächlich vollständig geladen ist. Theoretisch sollte das der Fall sein, sobald das DOM geladen ist, direkt bevor DOMContentLoaded feuert.

Die Ladezeit der Seite wird immer noch durch das Laden des Scripts verlängert. Der Benutzer kann Links noch nicht benutzen und noch nicht scrollen.

Script mit defer laden

DOMContentLoaded mit defer / async

Sowohl async als auch defer weisen den Browser an, die Seite zu laden und aufzubauen, und die Scripte im Hintergrund auszuführen. Die Scripte sollen den Aufbau des DOM und das Rendern der Seite nicht blockieren.

asyncdefer
Reihenfolge Mit async führt der Browser Scripte in der Reihenfolge zuerst geladen zuerst ausgeführt aus. Das zuerst geladene Script läuft zuerst, egal an welcher Stelle das Script im DOM erscheint. Mit defer führt der Browser die Script in der Reihenfolge aus, in der sie aufgeführt sind.
DOMContentLoaded Scripte mit async können geladen und ausgeführt werden während das Dokument noch nicht vollständig geladen wurde, etwa wenn das Script klein oder im Cache ist und das Dokument lang genug. Scripts mit defer müssen warten, bis das Dokument geladen und geparst ist, also bis DOMContentLoaded.

Script Insertion

Wenn Javascript nicht für den Aufbau der Seite benötigt wird, muss ein externes Script erst geladen werden, wenn das DOM und alle Elemente geladen sind.

Script nachladen

Dafür sorgen ein paar Zeilen Javascript am Ende der Seite.

Javascript nachladen
function loadScriptAfter() {
   let script = document.createElement("script");
   script.src = "script.js";
   document.body.appendChild(script);
}

if (window.addEventListener) {
   window.addEventListener("load", loadScriptAfter);
} else if (window.attachEvent) {
   window.attachEvent("onload", loadScriptAfter);
} else {
   window.onload = loadScriptAfter;
}

Mit async ist Script Insertion im Grunde genommen überflüssig geworden.

Ladezeiten verkürzen

Das Auslagern von Javascript in eine externe Scriptdatei kann die Performance von Webseiten verbessern. Wenn die Scripte auf mehreren Webseiten eingesetzt werden, liegen die Scripte beim Aufrufe einer weiteren Seite bereits im Cache des Browsers.

Wenn Design und Entwicklung der Webseite abgeschlossen sind, wurden früher externe Javascript-Dateien so weit wie möglich zu einer Script-Datei zusammengeführt. Weniger HTTP-Requests – das reduziert die Ladezeit.

Mit HTTP/2 fällt diese Optimierung weniger ins Gewicht, zudem hat uns ECMAScript 6 den Export und Import von Script-Dateien als Module mitgebracht.