Aufwärmen · Aufgabe 1 von 7
Aufwärmen aus Lektion I6.1: der Body einer POST-Anfrage. Was gibt das aus?
const body = JSON.stringify({title: "Dune", year: 1965});
console.log(typeof body, body);// I6.4 · ca. 36 Min. · Aufbau
Nach dieser Lektion halten Sie Einstellungen und kleine Listen mit localStorage im Browser, speichern Objekte sicher als JSON und sagen, wann IndexedDB die bessere Wahl ist.
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
const body = JSON.stringify({title: "Dune", year: 1965});
console.log(typeof body, body);Vorhersagen · Aufgabe 2 von 7
localStorage.setItem("count", 3);
const count = localStorage.getItem("count");
console.log(count + 1);Üben · Aufgabe 3 von 7
localStorage.____("theme", "dark");Üben · Aufgabe 4 von 7
console.log(localStorage.getItem("nothing"), localStorage.length);Üben · Aufgabe 5 von 7
localStorage.setItem("user", {name: "Ada"});
console.log(localStorage.getItem("user"));Denksport · Aufgabe 6 von 7
const cart = JSON.parse(localStorage.getItem("cart")) ?? [];
cart.push("Dune");
localStorage.setItem("cart", JSON.stringify(cart));
console.log(localStorage.getItem("cart"), typeof localStorage.getItem("cart"));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 zählt ihre Besuche und hält eine Leseliste in localStorage. main.js speichert eine Zahl und liest sie als String zurück, speichert ein Array als JSON und lädt es wieder und zeigt, was mit einem Wert passiert, der ohne JSON.stringify gespeichert wurde. Am Ende leert es den Speicher, damit jeder Lauf gleich beginnt.
main.js
// Numbers come back as strings.
const visits = Number(localStorage.getItem("visits") ?? 0) + 1;
localStorage.setItem("visits", visits);
console.log("visits:", localStorage.getItem("visits"), typeof localStorage.getItem("visits"));
console.log("missing key:", localStorage.getItem("nothing"));
// Arrays and objects go through JSON.
function loadBooks() {
try {
return JSON.parse(localStorage.getItem("books")) ?? [];
} catch {
return []; // broken data: start with an empty list
}
}
function saveBooks(books) {
localStorage.setItem("books", JSON.stringify(books));
}
saveBooks([...loadBooks(), "Dune", "Emma"]);
console.log("stored:", localStorage.getItem("books"));
const books = loadBooks();
console.log("loaded:", books, books.length);
localStorage.setItem("books", "Dune,Emma"); // saved without JSON.stringify
console.log("broken:", loadBooks());
document.querySelector("#visits").textContent = "Visit number " + visits;
const list = document.querySelector("#books");
for (const title of books) {
const item = document.createElement("li");
item.textContent = title;
list.append(item);
}
localStorage.clear();
console.log("after clear:", localStorage.length);
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Reading list</title>
</head>
<body>
<h1>My reading list</h1>
<p id="visits"></p>
<ul id="books"></ul>
<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
visits: 1 string
missing key: null
stored: ["Dune","Emma"]
loaded: [ 'Dune', 'Emma' ] 2
broken: []
after clear: 0
<!-- #books -->
<ul id="books"><li>Dune</li><li>Emma</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 3
Die Seite zeigt das Theme und hat die Buttons Light und Dark. Beim Laden zeigt sie schon das gespeicherte Theme oder light. Ein Klick zeigt das gewählte Theme, aber der Starter speichert es nie, daher ist die Seite nach einem Neuladen wieder hell. Speichern Sie die Auswahl unter dem Schlüssel "theme".
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: Nach einem Klick hat localStorage kein theme. Wo speichert die Seite überhaupt etwas?
Der Klick-Listener kennt das gewählte Theme.
localStorage.setItem("theme", theme); im Klick-Listener
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
const label = document.querySelector("#theme");
function show(theme) {
document.body.className = theme;
label.textContent = "Theme: " + theme;
}
show(localStorage.getItem("theme") ?? "light");
for (const theme of ["light", "dark"]) {
document.querySelector("#" + theme).addEventListener("click", () => {
show(theme);
localStorage.setItem("theme", theme);
});
}
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 label = document.querySelector("#theme");
function show(theme) {
document.body.className = theme;
label.textContent = "Theme: " + theme;
}
show(localStorage.getItem("theme") ?? "light");
for (const theme of ["light", "dark"]) {
document.querySelector("#" + theme).addEventListener("click", () => {
show(theme);
});
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {loadPage} from './learnrun.js';
const text = (page, selector) => page.document.querySelector(selector).textContent;
const click = (page, selector) => page.document.querySelector(selector).click();
test('Without a saved theme the page shows light', async () => {
const page = await loadPage();
assert.equal(text(page, '#theme'), 'Theme: light', `the page shows ${text(page, '#theme')}`);
});
test('A saved theme is shown on load', async () => {
const page = await loadPage({localStorage: {theme: 'dark'}});
assert.equal(text(page, '#theme'), 'Theme: dark', `the page shows ${text(page, '#theme')}`);
});
test('The chosen theme is still there after a reload', async () => {
const page = await loadPage();
click(page, '#dark');
assert.equal(page.window.localStorage.getItem('theme'), 'dark', `localStorage has theme ${page.window.localStorage.getItem('theme')}`);
const reloaded = await loadPage({localStorage: page.window.localStorage});
assert.equal(text(reloaded, '#theme'), 'Theme: dark', `after a reload the page shows ${text(reloaded, '#theme')}`);
});
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Theme</title>
</head>
<body>
<p id="theme"></p>
<button id="light" type="button">Light</button>
<button id="dark" type="button">Dark</button>
<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 3
Die Seite hält eine Leseliste unter dem Schlüssel "books". Sie lädt die Liste schon sicher und fügt bei jedem Klick auf Add einen Titel hinzu. Aber der Starter speichert das Array selbst, das localStorage zu "Dune,Emma" macht, daher lässt sich die Liste nach einem Neuladen nicht parsen und ist leer. Speichern Sie sie als JSON.
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: localStorage hat books = Dune,Emma. Was macht setItem mit einem Array?
Alles, was kein String ist, wird auf die Standardweise in einen umgewandelt. JSON behält die Klammern und Anführungszeichen.
localStorage.setItem("books", JSON.stringify(books));
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
const list = document.querySelector("#books");
const input = document.querySelector("#title");
function load() {
try {
return JSON.parse(localStorage.getItem("books")) ?? [];
} catch {
return []; // a broken value: start again
}
}
function render(books) {
list.replaceChildren(...books.map((title) => {
const item = document.createElement("li");
item.textContent = title;
return item;
}));
}
const books = load();
render(books);
document.querySelector("#add").addEventListener("click", () => {
books.push(input.value);
localStorage.setItem("books", JSON.stringify(books));
render(books);
input.value = "";
});
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 list = document.querySelector("#books");
const input = document.querySelector("#title");
function load() {
try {
return JSON.parse(localStorage.getItem("books")) ?? [];
} catch {
return []; // a broken value: start again
}
}
function render(books) {
list.replaceChildren(...books.map((title) => {
const item = document.createElement("li");
item.textContent = title;
return item;
}));
}
const books = load();
render(books);
document.querySelector("#add").addEventListener("click", () => {
books.push(input.value);
localStorage.setItem("books", books);
render(books);
input.value = "";
});
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {loadPage} from './learnrun.js';
const text = (page, selector) => page.document.querySelector(selector).textContent;
const click = (page, selector) => page.document.querySelector(selector).click();
const titles = (page) => [...page.document.querySelectorAll('#books li')].map((item) => item.textContent);
function add(page, title) {
page.document.querySelector('#title').value = title;
click(page, '#add');
}
test('Saved books are shown on load', async () => {
const page = await loadPage({localStorage: {books: '["Dune","Emma"]'}});
assert.deepEqual(titles(page), ['Dune', 'Emma'], `the list shows ${JSON.stringify(titles(page))}`);
});
test('A broken saved value gives an empty list', async () => {
const page = await loadPage({localStorage: {books: '{oops'}});
assert.deepEqual(titles(page), [], `the list shows ${JSON.stringify(titles(page))}`);
});
test('Added books are saved as JSON and survive a reload', async () => {
const page = await loadPage();
add(page, 'Dune');
add(page, 'Emma');
const saved = page.window.localStorage.getItem('books');
assert.equal(saved, '["Dune","Emma"]', `localStorage has books = ${saved}`);
const reloaded = await loadPage({localStorage: page.window.localStorage});
assert.deepEqual(titles(reloaded), ['Dune', 'Emma'], `after a reload the list shows ${JSON.stringify(titles(reloaded))}`);
});
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Reading list</title>
</head>
<body>
<input id="title" aria-label="Title">
<button id="add" type="button">Add</button>
<ul id="books"></ul>
<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 3 von 3
Die Seite zeigt einen Zähler und einen +1-Button und speichert den Zähler unter dem Schlüssel "count". Auf einer neuen Seite zählt sie 0, 1, 2. Aber bei einem gespeicherten Zähler von 4 zeigt ein Klick 41 statt 5: Der Starter addiert 1 zu dem String, den er gelesen hat. Machen Sie beim Laden der Seite aus dem gespeicherten Wert eine Zahl.
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: Ein gespeicherter Wert 4 wird zu 41. Welchen Typ liefert getItem zurück?
"4" + 1 hängt zwei Strings aneinander. Machen Sie den Wert einmal zu einer Zahl, wenn die Seite lädt.
let count = Number(localStorage.getItem("count") ?? 0);
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
const output = document.querySelector("#count");
let count = Number(localStorage.getItem("count") ?? 0);
output.textContent = count;
document.querySelector("#more").addEventListener("click", () => {
count = count + 1;
localStorage.setItem("count", count);
output.textContent = count;
});
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 output = document.querySelector("#count");
let count = localStorage.getItem("count") ?? 0;
output.textContent = count;
document.querySelector("#more").addEventListener("click", () => {
count = count + 1;
localStorage.setItem("count", count);
output.textContent = count;
});
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {loadPage} from './learnrun.js';
const text = (page, selector) => page.document.querySelector(selector).textContent;
const click = (page, selector) => page.document.querySelector(selector).click();
test('A new page starts at 0 and counts to 1', async () => {
const page = await loadPage();
assert.equal(text(page, '#count'), '0', `at the start the page shows ${text(page, '#count')}`);
click(page, '#more');
assert.equal(text(page, '#count'), '1', `after one click the page shows ${text(page, '#count')}`);
});
test('A saved count of 4 goes on with 5', async () => {
const page = await loadPage({localStorage: {count: '4'}});
click(page, '#more');
assert.equal(text(page, '#count'), '5', `after one click the page shows ${text(page, '#count')}`);
});
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Counter</title>
</head>
<body>
<p id="count"></p>
<button id="more" type="button">+1</button>
<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 books = JSON.parse(localStorage.getItem("books"));
document.querySelector("#out").textContent = books.length + " books";
Was Node.js ausgibt
TypeError: Cannot read properties of null (reading 'length')Warum, und die Lösung
Beim ersten Besuch ist noch nichts gespeichert. getItem liefert null, und JSON.parse(null) ist ebenfalls null, ohne Fehler, der Absturz kommt also erst eine Zeile später. Geben Sie einen Standardwert an: const books = JSON.parse(localStorage.getItem("books")) ?? [];.
localStorage.setItem("user", {name: "Ada"});
const user = JSON.parse(localStorage.getItem("user"));
document.querySelector("#out").textContent = user.name;
Was Node.js ausgibt
SyntaxError: "[object Object]" is not valid JSONWarum, und die Lösung
setItem speichert nur Strings, das Objekt wurde also zum Text "[object Object]", der kein JSON ist. Speichern Sie es mit localStorage.setItem("user", JSON.stringify(user)) und behalten Sie JSON.parse zum Laden.
localStorage.setItem("theme", "dark");
console.log(localStorage.getItem("theme"));
Was Node.js ausgibt
ReferenceError: localStorage is not definedWarum, und die Lösung
localStorage gehört zum Browser. In Node.js 24 gibt es es nur, wenn node mit --experimental-webstorage gestartet wird, node main.js kennt es also nicht. Führen Sie Speichercode in einer Seite aus (index.html mit main.js, von einem lokalen Webserver); in Node.js halten Sie Daten stattdessen mit node:fs in einer Datei.
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.