Aufwärmen · Aufgabe 1 von 7
Zum Aufwärmen aus Lektion B5.3: Was gibt dieser Code aus?
const list = ["tea", "milk"];
const more = [...list, "eggs"];
console.log(list.length, more.length);// B6.1 · ca. 30 Min. · Einstieg
Nach dieser Lektion finden Sie Elemente auf einer Seite, ändern ihren Text sicher und setzen Attribute, data-Werte und Klassen aus JavaScript.
Lektion 1 von 5 in B6 Das DOM und Ereignisse
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
const list = ["tea", "milk"];
const more = [...list, "eggs"];
console.log(list.length, more.length);Vorhersagen · Aufgabe 2 von 7
const msg = document.querySelector('#msg');
msg.textContent = '<b>Bye</b>';
console.log(msg.children.length, msg.textContent);Üben · Aufgabe 3 von 7
const title = document.____('#title');
console.log(title.textContent);Üben · Aufgabe 4 von 7
Üben · Aufgabe 5 von 7
const li = document.querySelector('#list li');
li.classList.add('on');
li.classList.remove('tag');
console.log(li.classList.contains('tag'), li.className);Denksport · Aufgabe 6 von 7
const items = document.querySelectorAll('#list li');
const list = document.querySelector('#list');
list.append(document.createElement('li'));
console.log(items.length, list.children.length);Anwenden · Aufgabe 7 von 7
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Die Seite hat eine Überschrift, eine Liste mit drei Büchern und ihren Seitenzahlen in data-pages sowie einen Hinweis. main.js wählt die Einträge aus, macht aus der NodeList ein Array, um Namen und Zahlen zu lesen, aktualisiert die Überschrift, markiert lange Bücher mit einer Klasse und setzt zwei Attribute. Dann schreibt es denselben String einmal mit textContent und einmal mit innerHTML in den Hinweis, damit Sie den Unterschied sehen, und fragt nach einem Element, das es nicht gibt.
main.js
// main.js runs after the HTML is parsed: module scripts are deferred.
const title = document.querySelector('#title');
const items = document.querySelectorAll('#books li');
console.log(items.length, typeof items.map);
// A NodeList has no map, so spread it into an array first.
const names = [...items].map((li) => li.textContent);
console.log(names);
const pages = [...items].map((li) => Number(li.dataset.pages));
console.log(pages);
title.textContent = 'My books (' + items.length + ')';
console.log(title.textContent);
for (const li of items) {
if (Number(li.dataset.pages) > 400) li.classList.add('long');
}
items[0].dataset.read = 'yes';
items[2].setAttribute('title', 'A short one');
console.log(items[0].getAttribute('data-read'), items[1].classList.contains('long'));
// textContent shows text as it is; innerHTML reads it as HTML.
const note = document.getElementById('note');
note.textContent = '<b>2</b> long books';
console.log(note.textContent, note.children.length);
note.innerHTML = '<b>2</b> long books';
console.log(note.textContent, note.children.length);
console.log(note.classList.toggle('hint'), note.classList.length);
console.log(document.querySelector('#missing'));
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Reading list</title>
</head>
<body>
<h1 id="title">My books</h1>
<ul id="books"><li data-pages="412">Dune</li><li data-pages="474">Emma</li><li data-pages="96">Animal Farm</li></ul>
<p id="note" class="hint">Loading...</p>
<script type="module" src="main.js"></script>
</body>
</html>
Um die Seite auf Ihrem Computer auszuführen, öffnen Sie index.html über einen lokalen Webserver, nicht als Datei: Browser führen Modul-Skripte von file://-Adressen nicht aus. Für die Prüfungen installieren Sie zuerst jsdom (die Befehle unten).
Ausgabe
3 undefined
[ 'Dune', 'Emma', 'Animal Farm' ]
[ 412, 474, 96 ]
My books (3)
yes true
<b>2</b> long books 0
2 long books 1
false 0
null
<!-- #books -->
<ul id="books"><li data-pages="412" class="long" data-read="yes">Dune</li><li data-pages="474" class="long">Emma</li><li data-pages="96" title="A short one">Animal Farm</li></ul>Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Ihre Seite
Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.
Führen Sie das Programm aus, um seine Seite hier zu sehen.
Übung 1 von 2
Die Seite listet vier Aufgaben, jede mit data-done="yes" oder data-done="no". main.js soll jeder erledigten Aufgabe die Klasse done geben, und keiner anderen, und den Absatz #summary auf "2 of 4 done" setzen, gezählt auf der Seite. Der Starter markiert nur den ersten Eintrag und schreibt eine feste Zusammenfassung. Wählen Sie alle Einträge aus, verteilen Sie sie in ein Array und verwenden Sie dataset und classList.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Ihre Seite
Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.
Führen Sie das Programm aus, um seine Seite hier zu sehen.
Führen Sie die Tests aus: Nur die erste Aufgabe ist markiert, und die Zusammenfassung ist fester Text.
[...document.querySelectorAll('#tasks li')] ergibt ein Array, das Sie durchlaufen und filtern können.
task.dataset.done ist der String 'yes' oder 'no'; zählen Sie mit filter(...).length und bauen Sie den Text aus dieser Zahl und tasks.length.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
const tasks = [...document.querySelectorAll('#tasks li')];
for (const task of tasks) {
if (task.dataset.done === 'yes') task.classList.add('done');
}
const done = tasks.filter((task) => task.dataset.done === 'yes').length;
document.querySelector('#summary').textContent = done + ' of ' + tasks.length + ' done';
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
const task = document.querySelector('#tasks li');
task.classList.add('done');
// TODO: mark every finished task, then count them for the summary.
document.querySelector('#summary').textContent = '1 of 1 done';
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {loadPage} from './learnrun.js';
const marked = (page) => [...page.document.querySelectorAll('#tasks li')].map((li) => li.classList.contains('done'));
test('Jede erledigte Aufgabe hat die Klasse done', async () => {
const page = await loadPage();
const got = marked(page);
assert.equal(got[0] && got[2], true, 'die Klasse done steht an diesen Aufgaben: ' + JSON.stringify(got) + ', erwartet an der 1. und 3.');
});
test('Offene Aufgaben bekommen die Klasse done nicht', async () => {
const page = await loadPage();
const got = marked(page);
assert.deepEqual(got, [true, false, true, false], 'die Klasse done steht an diesen Aufgaben: ' + JSON.stringify(got));
});
test('Die Zusammenfassung lautet 2 of 4 done', async () => {
const page = await loadPage();
const text = page.document.querySelector('#summary').textContent;
assert.equal(text, '2 of 4 done', 'die Zusammenfassung lautet ' + JSON.stringify(text));
});
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Today</title>
</head>
<body>
<h1>Today</h1>
<ul id="tasks">
<li data-done="yes">Buy milk</li>
<li data-done="no">Call Ada</li>
<li data-done="yes">Water the plants</li>
<li data-done="no">Book tickets</li>
</ul>
<p id="summary"></p>
<script type="module" src="main.js"></script>
</body>
</html>
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
Um die Seite auf Ihrem Computer auszuführen, öffnen Sie index.html über einen lokalen Webserver, nicht als Datei: Browser führen Modul-Skripte von file://-Adressen nicht aus. Für die Prüfungen installieren Sie zuerst jsdom (die Befehle unten).
Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):
npm install jsdom@28.1.0
node --testlearnrun.js herunterladenÜbung 2 von 2
Das Profil stammt aus einem Anmeldeformular, jeder Wert ist also Text, den ein Nutzer eingegeben hat, und die Bio enthält Markup. Füllen Sie die Karte: #name und #bio zeigen die Werte genau, als Text, ohne daraus gebaute Elemente; das Bild #photo bekommt src mit dem Foto und alt mit "Photo of " plus dem Namen; die Karte bekommt data-role mit der Rolle und die Klasse admin, wenn die Rolle "admin" ist, und behält ihre Klasse card. Der Starter verwendet innerHTML und lässt den Rest offen.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.
Ihre Seite
Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.
Führen Sie das Programm aus, um seine Seite hier zu sehen.
Führen Sie die Tests aus: Die Bio zeigt "I love maths" fett, weil innerHTML aus dem Text ein <b>-Element gebaut hat.
Ersetzen Sie innerHTML durch textContent und verwenden Sie dann photo.setAttribute('src', …) und photo.setAttribute('alt', …).
card.dataset.role = profile.role schreibt data-role; classList.add('admin') fügt eine Klasse hinzu und behält card.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
// Values a user typed into a sign-up form: treat them as text.
const profile = { name: 'Ada Lovelace', bio: 'I love <b>maths</b>', role: 'admin', photo: 'ada.png' };
const card = document.querySelector('#card');
document.querySelector('#name').textContent = profile.name;
document.querySelector('#bio').textContent = profile.bio;
const photo = document.querySelector('#photo');
photo.setAttribute('src', profile.photo);
photo.setAttribute('alt', 'Photo of ' + profile.name);
card.dataset.role = profile.role;
if (profile.role === 'admin') card.classList.add('admin');
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
// Values a user typed into a sign-up form: treat them as text.
const profile = { name: 'Ada Lovelace', bio: 'I love <b>maths</b>', role: 'admin', photo: 'ada.png' };
const card = document.querySelector('#card');
document.querySelector('#name').innerHTML = profile.name;
document.querySelector('#bio').innerHTML = profile.bio;
// TODO: the photo and its alt text, data-role, and the class admin.
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {loadPage} from './learnrun.js';
test('Die Bio erscheint als Text, nicht als HTML', async () => {
const page = await loadPage();
const bio = page.document.querySelector('#bio');
assert.equal(bio.children.length, 0, 'die Bio enthält ' + bio.children.length + ' aus dem Text gebaute(s) Element(e), erwartet keines');
assert.equal(bio.textContent, 'I love <b>maths</b>', 'die Bio zeigt ' + JSON.stringify(bio.textContent));
});
test('Der Name wird angezeigt', async () => {
const page = await loadPage();
const text = page.document.querySelector('#name').textContent;
assert.equal(text, 'Ada Lovelace', 'der Name zeigt ' + JSON.stringify(text));
});
test('Das Foto hat src und Alternativtext', async () => {
const page = await loadPage();
const photo = page.document.querySelector('#photo');
assert.equal(photo.getAttribute('src'), 'ada.png', 'src ist ' + JSON.stringify(photo.getAttribute('src')));
assert.equal(photo.getAttribute('alt'), 'Photo of Ada Lovelace', 'alt ist ' + JSON.stringify(photo.getAttribute('alt')));
});
test('Die Karte hat data-role und die Klassen card und admin', async () => {
const page = await loadPage();
const card = page.document.querySelector('#card');
assert.equal(card.getAttribute('data-role'), 'admin', 'data-role ist ' + JSON.stringify(card.getAttribute('data-role')));
assert.equal(card.classList.contains('admin'), true, 'die Karte hat keine Klasse admin');
assert.equal(card.classList.contains('card'), true, 'die Karte hat ihre Klasse card verloren');
});
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Profile</title>
</head>
<body>
<article id="card" class="card">
<img id="photo" src="blank.png" alt="">
<h2 id="name">Name</h2>
<p id="bio"></p>
</article>
<script type="module" src="main.js"></script>
</body>
</html>
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
Um die Seite auf Ihrem Computer auszuführen, öffnen Sie index.html über einen lokalen Webserver, nicht als Datei: Browser führen Modul-Skripte von file://-Adressen nicht aus. Für die Prüfungen installieren Sie zuerst jsdom (die Befehle unten).
Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):
npm install jsdom@28.1.0
node --testlearnrun.js herunterladenconst title = document.querySelector('#titel');
title.textContent = 'My tags';
Was Node.js ausgibt
TypeError: Cannot set properties of null (setting 'textContent')Warum, und die Lösung
Die id ist #titel geschrieben, und kein Element hat sie, also hat querySelector null zurückgegeben. Der Fehler kommt eine Zeile später, wenn das Programm eine Eigenschaft von null setzt. Vergleichen Sie den Selektor Zeichen für Zeichen mit index.html: Hier muss es #title heißen. Wenn ein Element wirklich fehlen kann, prüfen Sie zuerst: if (title) { … }. Firefox und Safari formulieren diese Meldung anders.
const tags = document.querySelectorAll('.tag');
const names = tags.map((li) => li.textContent);
console.log(names);
Was Node.js ausgibt
TypeError: tags.map is not a functionWarum, und die Lösung
querySelectorAll gibt eine NodeList zurück, die wie ein Array aussieht, aber keines ist: Sie hat length, Indizes und forEach, aber kein map, filter oder reduce. Verteilen Sie sie zuerst in ein echtes Array: [...tags].map((li) => li.textContent). Eine for...of-Schleife über die NodeList funktioniert ebenfalls.
const tags = document.querySelectorAll('.tag');
tags.classList.add('on');
Was Node.js ausgibt
TypeError: Cannot read properties of undefined (reading 'add')Warum, und die Lösung
classList gehört zu jedem einzelnen Element, nicht zur Liste, also ist tags.classList undefined, und add darauf aufzurufen scheitert. Ändern Sie jedes Element in einer Schleife: for (const tag of tags) tag.classList.add('on'). Wenn Sie nur das erste meinen, nehmen Sie querySelector, das ein einzelnes Element zurückgibt.
JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.