Tutorials › Teil 1: Grundlagen

Kapitel 2 von 16

Skripte in eine Seite einbinden

Hier ist eine vollständige Seite. Nichts fehlt, du kannst das in eine Datei namens versuch.html kopieren und doppelklicken.

<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <title>Versuch</title>
</head>
<body>
  <p id="ziel">Noch nichts passiert.</p>
  <script>
    document.getElementById("ziel").textContent = "Doch, etwas ist passiert.";
  </script>
</body>
</html>

Sieben Zeilen Gerüst, drei Zeilen Inhalt. Das ist die kleinste sinnvolle Einheit, und alles Weitere in diesem Kapitel dreht sich um die Frage, wo genau das <script> hingehört.

Zwei Sorten script-Element

Entweder der Code steht direkt zwischen den Tags:

<script>
  console.log("Ich stehe im Dokument.");
</script>

Oder er steht in einer eigenen Datei, und das Element verweist nur darauf:

<script src="/assets/js/meins.js"></script>

Das schließende </script> musst du auch bei der zweiten Form hinschreiben. Ein <script src="..." /> funktioniert nicht, HTML erlaubt das für dieses Element nicht.

Ab wann lohnt die eigene Datei? Sobald derselbe Code auf mehr als einer Seite gebraucht wird, spätestens. Der Browser lädt sie dann einmal und holt sie beim nächsten Aufruf aus dem Zwischenspeicher.

Die Reihenfolge ist das ganze Problem

Der Browser arbeitet ein Dokument von oben nach unten ab. Trifft er auf ein <script>, hält er an, führt es aus und macht danach weiter. Alles, was weiter unten im HTML steht, existiert zu diesem Zeitpunkt noch nicht.

Deshalb geht das hier schief:

<head>
  <script>
    const p = document.getElementById("ziel");
    document.body.insertAdjacentHTML("beforeend",
      "<p>Gefunden? " + (p === null ? "Nein, null." : "Ja.") + "</p>");
  </script>
</head>
<body>
  <p id="ziel">Ich stehe zu weit unten.</p>
</body>

Der Absatz mit der Kennung ziel steht im Dokument unter dem Skript. Als das Skript lief, hatte der Browser ihn noch nicht gelesen. getElementById liefert deshalb null, und wer damit weiterarbeitet, bekommt kurz darauf eine Fehlermeldung.

Es gibt drei Auswege, und einer davon ist heute der übliche.

defer

Schreib das Skript in den <head> und häng defer an:

<head>
  <script src="/assets/js/meins.js" defer></script>
</head>

defer sagt dem Browser: Lad die Datei ruhig jetzt schon herunter, aber führ sie erst aus, wenn das Dokument komplett gelesen ist. Das Herunterladen läuft parallel zum Aufbau der Seite, die Ausführung passiert danach.

Mehrere Skripte mit defer laufen in der Reihenfolge, in der sie im HTML stehen. Darauf kann man sich verlassen.

Für so gut wie jeden Fall ist das die richtige Wahl.

async

Sieht ähnlich aus, verhält sich anders:

<script src="/assets/js/zaehler.js" async></script>

async heißt: Lad herunter und führ sofort aus, sobald die Datei da ist. Der Seitenaufbau wird dafür unterbrochen. Wann das passiert, hängt davon ab, wie schnell die Datei ankommt, und die Reihenfolge mehrerer async-Skripte ist offen.

Sinnvoll ist das nur für Code, der von nichts abhängt und von dem nichts abhängt. Zählpixel etwa. Sonst finger weg.

Ans Ende des body

Der dritte Weg, und der einzige, den die alten Skripte in diesem Archiv kennen:

<body>
  <p id="ziel">Text</p>
  <script src="/assets/js/meins.js"></script>
</body>

Funktioniert einwandfrei. Wenn der Browser hier ankommt, steht der Rest der Seite. Nachteil gegenüber defer: Der Download beginnt erst, wenn der Parser das Element erreicht, also später.

Kein Grund, bestehenden Code umzubauen. Bei neuem Code nimm defer.

type="module"

Es gibt eine vierte Variante, und die ist die modernste:

<script type="module" src="/assets/js/haupt.js"></script>

Ein Modul verhält sich automatisch wie defer, ein zusätzliches Attribut braucht es nicht. Zusätzlich bekommt es einen eigenen Namensraum. Was du in einem Modul mit const anlegst, existiert nur dort und kollidiert nicht mit gleichnamigen Dingen aus anderen Dateien.

Und es kann Code aus anderen Dateien holen:

// werkzeug.js
export function wuerfeln() {
  return Math.floor(Math.random() * 6) + 1;
}

// haupt.js
import { wuerfeln } from "./werkzeug.js";
console.log(wuerfeln());

Einen Haken hat die Sache. Module unterliegen strengeren Sicherheitsregeln, und der Browser weigert sich, sie über file:// zu laden. Ein Doppelklick auf die HTML-Datei reicht dafür nicht mehr, du brauchst einen kleinen lokalen Webserver. Mit Python ist das eine Zeile:

python3 -m http.server 8000

Danach liegt deine Datei unter http://localhost:8000/versuch.html.

Für die ersten Kapitel dieses Kurses brauchst du das nicht. Ab Kapitel 12, wenn der Code länger wird, ist es die Mühe wert.

Wo nichts hingehört

Zwei Dinge, die du in alten Beispielen sehen wirst und nicht übernehmen solltest.

<script language="javascript"> stammt aus dem letzten Jahrtausend. Das Attribut ist längst aus dem Standard geflogen. Auch type="text/javascript" kannst du weglassen, das ist seit HTML5 die Vorgabe.

Und die Kommentarklammern:

<script>
<!--
  alert("Hallo");
// -->
</script>

Die waren dafür gedacht, dass Browser aus den frühen Neunzigern den Code nicht als Text auf die Seite schreiben. Solche Browser gibt es nicht mehr. In diesem Archiv stehen sie trotzdem in fast jedem Skript, weil sie über zwanzig Jahre mitkopiert wurden.

Im nächsten Kapitel geht es endlich um den Code selbst.