Abschnitt 11 · Lektion 5
Elemente erstellen und einhängen
Bisher hat der Kurs verändert, was schon da war. Jetzt kommt etwas dazu, das im Markup gar nicht steht.
Erzeugen und einhängen sind zwei Schritte
<ul id="liste"><li>Schon da</li></ul> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
.eintrag {
margin-bottom: 0.25rem;
} const liste = document.getElementById("liste");
// createElement erzeugt ein Element, das noch nirgends hängt.
const eintrag = document.createElement("li");
console.log("Elternteil vorher:", eintrag.parentElement);
// Füllen, solange es noch draußen ist.
eintrag.textContent = "Neu dazu";
eintrag.classList.add("eintrag");
eintrag.dataset.id = "neu";
// Erst jetzt in den Baum.
liste.append(eintrag);
console.log("Elternteil nachher:", eintrag.parentElement.tagName);
console.log("Einträge:", liste.children.length);
// append nimmt auch reinen Text und mehrere Sachen auf einmal.
const zweiter = document.createElement("li");
zweiter.append("Text ohne Umweg");
liste.append(zweiter, document.createElement("li"));
console.log("jetzt:", liste.children.length); document.createElement("li") erzeugt ein Element. Es existiert danach als Objekt, hat aber keinen Platz auf der Seite: sein parentElement ist null, und zu sehen ist nichts.
Sichtbar wird es erst, wenn du es in den Baum hängst. liste.append(eintrag) hängt es als letztes Kind an.
Dass „kein Platz” wörtlich gemeint ist, lässt sich messen. Tipp in die Eingabezeile der Console document.createElement("li").getBoundingClientRect().width, und die Antwort ist 0: kein Platz, keine Breite. eintrag.getBoundingClientRect().width, also das Element aus dem Beispiel nach dem Einhängen, antwortet mit einer echten Breite, in der schmalen Spalte 308. Dazwischen liegt nur das append. Kürzer sagt es isConnected: eintrag.isConnected antwortet true, ein frisch erzeugtes Element false.
Dass das zwei Schritte sind, ist kein Umstand, sondern der Sinn der Sache. Fülle das Element, solange es noch draußen ist. Text setzen, Klassen vergeben, Attribute schreiben: solange das Element nirgends hängt, muss der Browser nichts neu zeichnen. Erst das Einhängen kostet ihn Arbeit, und dann genau einmal.
Wer es andersherum macht, also erst einhängt und dann füllt, bekommt dasselbe Ergebnis. Der Unterschied fällt bei drei Einträgen nicht auf und bei dreihundert schon.
append nimmt außerdem mehr als ein Element: mehrere Argumente auf einmal, und reinen Text ohne Umweg über einen Textknoten.
Wohin genau
<ul id="liste">
<li id="mitte">Mitte</li>
</ul> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
} const liste = document.getElementById("liste");
const mitte = document.getElementById("mitte");
function neu(text) {
const el = document.createElement("li");
el.textContent = text;
return el;
}
// append und prepend hängen INNEN an, ans Ende oder an den Anfang.
liste.append(neu("ganz unten"));
liste.prepend(neu("ganz oben"));
// before und after hängen DANEBEN, als Geschwister.
mitte.before(neu("direkt davor"));
mitte.after(neu("direkt danach"));
for (const el of liste.children) {
console.log(el.textContent);
} Vier Methoden decken alle Richtungen ab, und die Namen sagen, was sie tun:
append hängt innen ans Ende, prepend innen an den Anfang. Beide arbeiten mit den Kindern des Elements, an dem du sie aufrufst.
before und after hängen daneben, als Geschwister. Sie rufst du am Nachbarn auf, nicht am Elternteil.
Der Unterschied ist derselbe wie zwischen „leg es in die Schachtel” und „stell es neben die Schachtel”.
appendChild kommt in diesem Kurs nicht vor. Es ist der ältere Weg, kann nur ein einziges Element und keinen Text, und append kann alles, was es kann. In fremdem Code wirst du es trotzdem oft sehen, deshalb hier der eine Satz: Es macht dasselbe wie append mit einem Argument.
Ein Muster im Markup statt im Code
<template id="muster">
<article class="karte">
<h3 class="titel"></h3>
<p class="preis"></p>
</article>
</template>
<div id="ziel"></div> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
h3 {
font-size: 1rem;
margin: 1rem 0 0.25rem;
}
p {
margin: 0 0 0.75rem;
} const muster = document.getElementById("muster");
const ziel = document.getElementById("ziel");
// Was in einem template steht, zeigt der Browser nicht an.
// Es liegt bereit, bis jemand es kopiert.
const daten = [
{ titel: "HTML", preis: "kostenlos" },
{ titel: "JavaScript", preis: "49,00 €" },
];
for (const eintrag of daten) {
// true heißt: mit allem, was darin steckt.
const karte = muster.content.firstElementChild.cloneNode(true);
karte.querySelector(".titel").textContent = eintrag.titel;
karte.querySelector(".preis").textContent = eintrag.preis;
ziel.append(karte);
}
console.log("Karten:", ziel.children.length);
// Ohne true bekommst du die leere Hülle ohne Inhalt.
const flach = muster.content.firstElementChild.cloneNode();
console.log("flach kopiert:", flach.outerHTML); Sobald ein neues Element mehr als zwei Zeilen hat, wird das Zusammenbauen im JavaScript unübersichtlich. Dafür gibt es <template>.
Was in einem template steht, zeigt der Browser nicht an. Es liegt bereit, und template.content ist der Zugang dazu. Mit cloneNode(true) machst du eine Kopie und füllst sie.
Das true ist wichtig: ohne Argument bekommst du nur das äußere Element, ohne seinen Inhalt. Mit true kommt der ganze Teilbaum mit.
Der Gewinn ist, dass die Struktur im Markup steht, wo sie hingehört, und JavaScript nur noch die Werte einsetzt. Wer das Aussehen der Karte ändern will, muss dafür kein JavaScript anfassen.
Und warum nicht einfach innerHTML
Weil man dann Text zusammenklebt, statt Elemente zu bauen. Sobald ein Wert aus einer Eingabe oder aus dem Netz kommt, ist das die Lücke aus Lektion 11.1, und zwar genau da, wo man sie am ehesten übersieht: mitten in einer Schleife über fremde Daten.
Mit createElement und textContent kann das nicht passieren. Ein Text, der wie Markup aussieht, bleibt Text. Deshalb baut dieser Kurs Listen so, und Lektion 11.7 macht daraus ein festes Muster.
Zum Mitnehmen
Erst erzeugen und füllen, dann einhängen. Solange das Element noch nirgends hängt, kostet jede Änderung daran den Browser nichts.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.