Ein klarer Schritt-für-Schritt‑Leitfaden zum Erstellen einer kleinen To‑Do‑App: Funktionen planen, Bildschirme anlegen, Logik hinzufügen, Daten speichern, testen und veröffentlichen.

Wenn ich in diesem Leitfaden „App" sage, meine ich eine kleine Web-App: eine einzelne Seite, die du in einem Browser öffnest und die auf Klicks und Eingaben reagiert. Keine Installation, keine Accounts, kein großes Setup — nur ein einfaches Projekt, das du lokal ausführen kannst.
Am Ende hast du eine To‑Do‑App, die:
localStorage (damit ein Tab‑Schließen nicht alles löscht)Sie wird nicht perfekt oder „Enterprise‑tauglich“ sein — und das ist auch gut so. Das ist ein Anfängerprojekt, das die Grundlagen vermittelt, ohne auf viele Tools zurückzugreifen.
Du baust die App Schritt für Schritt und lernst die Kernstücke, wie Front‑End Web‑Apps funktionieren:
Halte es einfach. Du brauchst nur:
Wenn du einen Ordner anlegen und ein paar Dateien bearbeiten kannst, bist du startklar.
Bevor du eine Zeile Code schreibst, überlege, wie „Erfolg" aussieht. Dieses Tutorial baut eine kleine App mit einer klaren Aufgabe: dir helfen, Aufgaben zu verfolgen.
Schreibe ein Ein-Satz-Ziel, das du beim Bauen vor Augen halten kannst:
„Diese App lässt mich Aufgaben in eine Liste eintragen, damit ich sie nicht vergesse."
Das ist alles. Wenn du Lust bekommst, Kalender, Erinnerungen, Tags oder Accounts hinzuzufügen, schiebe diese Ideen auf später.
Mach zwei kurze Listen:
Muss‑Funktionen (für dieses Projekt):
Nice‑to‑have (heute nicht nötig): Fälligkeitsdaten, Prioritäten, Kategorien, Suche, Drag‑and‑Drop, Cloud‑Sync.
Wenn die „Muss‑Funktionen" klein bleiben, finishst du das Projekt eher.
Die App kann eine einzelne Seite haben mit:
Sei konkret, damit du nicht hängen bleibst:
Wenn das klar ist, bist du bereit, die Projektdateien anzulegen.
Bevor wir Code schreiben, legen wir einen sauberen „Zuhause“-Ordner für die App an. Eine übersichtliche Struktur von Anfang an macht die nächsten Schritte leichter.
Erzeuge einen neuen Ordner auf deinem Rechner, z. B. todo-app. Dieser Ordner enthält alles für dieses Projekt.
Lege in diesem Ordner drei Dateien an:
index.html (Seitenstruktur)styles.css (Aussehen und Layout)app.js (Verhalten und Interaktivität)Wenn dein Betriebssystem Dateiendungen versteckt, achte darauf, dass du echte Dateien erstellst. Ein häufiger Anfängerfehler ist index.html.txt.
Öffne den todo-app-Ordner in deinem Editor (VS Code, Sublime Text etc.). Dann öffne index.html im Browser.
Deine Seite kann jetzt noch leer aussehen — das ist in Ordnung. Wir füllen sie im nächsten Schritt.
Wenn du Dateien änderst, aktualisiert der Browser die Seite nicht automatisch (außer du nutzt ein Tool dafür).
Die einfache Schleife ist:
Wenn etwas „nicht funktioniert", ist Neuladen der erste Schritt.
Du kannst die App per Doppelklick auf index.html bauen, aber ein lokaler Server vermeidet später seltsame Probleme (besonders beim Speichern oder Laden von Dateien).
Einsteigerfreundliche Optionen:
python -m http.server
Öffne dann die Adresse, die ausgegeben wird (oft http://localhost:8000) im Browser.
Jetzt erstellen wir ein sauberes Gerüst für die App. Dieses HTML macht noch nichts interaktiv (das kommt später), gibt aber JavaScript klare Stellen zum Lesen und Schreiben.
Enthalten sein sollten:
Einfache, lesbare IDs/Klassen helfen später, weil dein JavaScript Elemente ohne Verwirrung auswählt.
index.html ein\u003c!doctype html\u003e
\u003chtml lang=\"en\"\u003e
\u003chead\u003e
\u003cmeta charset=\"utf-8\" /\u003e
\u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e
\u003ctitle\u003eTo‑Do App\u003c/title\u003e
\u003clink rel=\"stylesheet\" href=\"styles.css\" /\u003e
\u003c/head\u003e
\u003cbody\u003e
\u003cmain class=\"app\" id=\"app\"\u003e
\u003ch1 class=\"app__title\" id=\"appTitle\"\u003eMy To‑Do List\u003c/h1\u003e
\u003cform class=\"task-form\" id=\"taskForm\"\u003e
\u003clabel class=\"task-form__label\" for=\"taskInput\"\u003eNew task\u003c/label\u003e
\u003cdiv class=\"task-form__row\"\u003e
\u003cinput
id=\"taskInput\"
class=\"task-form__input\"
type=\"text\"
placeholder=\"e.g., Buy milk\"
autocomplete=\"off\"
/\u003e
\u003cbutton id=\"addButton\" class=\"task-form__button\" type=\"submit\"\u003e
Add
\u003c/button\u003e
\u003c/div\u003e
\u003c/form\u003e
\u003cul class=\"task-list\" id=\"taskList\" aria-label=\"Task list\"\u003e\u003c/ul\u003e
\u003c/main\u003e
\u003cscript src=\"app.js\"\u003e\u003c/script\u003e
\u003c/body\u003e
\u003c/html\u003e
Das war's fürs Gerüst. Beachte, dass wir id="taskInput" und id="taskList" verwendet haben — das sind die beiden Elemente, mit denen du am meisten in JavaScript arbeitest.
Zurzeit existiert deine Seite, sieht aber vermutlich wie ein einfaches Dokument aus. Ein wenig CSS macht sie benutzerfreundlicher: klarere Abstände, gut lesbare Schrift und klickbare Knöpfe.
Ein zentriertes Kästchen hält die App fokussiert und verhindert, dass sich Inhalte über breite Bildschirme ausdehnen.
/* Basic page setup */
body {
font-family: Arial, sans-serif;
background: #f6f7fb;
margin: 0;
padding: 24px;
}
/* Centered app container */
.container {
max-width: 520px;
margin: 0 auto;
background: #ffffff;
padding: 16px;
border-radius: 10px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
}
Jede Aufgabe sollte wie eine eigene „Zeile" aussehen, mit angenehmem Abstand.
ul { list-style: none; padding: 0; margin: 16px 0 0; }
li {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
border: 1px solid #e7e7ee;
border-radius: 8px;
margin-bottom: 10px;
}
.task-text { flex: 1; }
Wenn eine Aufgabe abgeschlossen ist, sollte sie sich optisch verändern, damit man das auf einen Blick erkennt.
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
Halte Buttons in gleicher Größe und Stil, damit sie wie aus einer Hand wirken.
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
Das reicht für eine saubere, freundliche UI — keine fortgeschrittenen Tricks nötig. Als Nächstes verbinden wir das Verhalten mit JavaScript.
Jetzt, wo Eingabe, Knopf und Liste auf der Seite sind, bringen wir sie zum Leben. Ziel: Wenn jemand eine Aufgabe eingibt und Hinzufügen klickt (oder Enter drückt), erscheint ein neues Element in der Liste.
In deiner JavaScript‑Datei holst du zuerst die Elemente, dann hörst du auf zwei Aktionen: einen Klick auf den Knopf und die Enter‑Taste im Eingabefeld.
const taskInput = document.querySelector('#taskInput');
const addBtn = document.querySelector('#addBtn');
const taskList = document.querySelector('#taskList');
function addTask() {
const text = taskInput.value.trim();
// Block empty tasks (including ones that are just spaces)
if (text === '') return;
const li = document.createElement('li');
li.textContent = text;
taskList.appendChild(li);
// Clear the input and put the cursor back
taskInput.value = '';
taskInput.focus();
}
addBtn.addEventListener('click', addTask);
taskInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
addTask();
}
});
trim(), um Leerzeichen am Anfang/Ende zu entfernen.<li>, setzt den Text und fügt es der Liste hinzu.Wenn nichts passiert, prüfe, ob die IDs in HTML genau mit den Selektoren in JavaScript übereinstimmen — das ist einer der häufigsten Anfängerfehler.
Nachdem du Aufgaben hinzufügen kannst, machen wir sie interaktiv: du sollst Aufgaben als erledigt markieren und entfernen können.
Statt Aufgaben bloß als Strings zu halten, speichere sie als Objekte. So hat jede Aufgabe eine stabile Identität und einen Platz für den „done"‑Status:
text: der Aufgabentextdone: true oder falseid: eine eindeutige Zahl, damit wir die richtige Aufgabe finden/löschen könnenZum Beispiel:
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
Wenn du jede Aufgabe renderst, baue entweder ein Kontrollkästchen oder einen „Done"‑Knopf ein, plus einen „Delete"‑Knopf.
Ein Event Listener ist einfach „eine Art, auf Klicks zu reagieren". Am Anfängerfreundlichsten ist Event Delegation: Ein Klick‑Listener sitzt auf dem Container der Liste und prüft, was genau angeklickt wurde.
function toggleDone(id) {
tasks = tasks.map(t => t.id === id ? { ...t, done: !t.done } : t);
renderTasks();
}
function deleteTask(id) {
tasks = tasks.filter(t => t.id !== id);
renderTasks();
}
document.querySelector("#taskList").addEventListener("click", (e) => {
const id = Number(e.target.dataset.id);
if (e.target.matches(".toggle")) toggleDone(id);
if (e.target.matches(".delete")) deleteTask(id);
});
In deiner renderTasks()‑Funktion:
data-id="${task.id}" zu jedem Knopf hinzu..done).Momentan hat deine To‑Do‑Liste ein nerviges Problem: Beim Neuladen der Seite (oder Schließen des Tabs) ist alles weg.
Das passiert, weil die Aufgaben nur im JavaScript‑Speicher existieren. Beim Neuladen wird dieser Speicher zurückgesetzt.
localStorage ist im Browser eingebaut. Stell es dir als kleine Box vor, in der du Text unter einem Schlüssel ablegst. Ideal für Einsteigerprojekte, weil kein Server oder Account nötig ist.
Wir speichern die gesamte Aufgabenliste als JSON‑Text und laden sie beim Öffnen der Seite wieder.
Wann immer du eine Aufgabe hinzufügst, als erledigt markierst oder löschst, rufe saveTasks() auf.
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
Überall dort, wo du das tasks‑Array änderst, mach das direkt danach:
saveTasks(tasks);
renderTasks(tasks);
Beim Laden der Seite liest du den gespeicherten Wert. Wenn noch nichts gespeichert ist, fall zurück auf eine leere Liste.
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
Fertig: Deine App merkt sich jetzt Aufgaben über Seiten‑Reloads hinweg.
Tipp: localStorage speichert nur Strings, daher wandelt JSON.stringify() das Array in Text und JSON.parse() macht aus dem Text wieder ein Array.
Testen klingt langweilig, ist aber der schnellste Weg, aus „funktioniert bei mir" ein „funktioniert immer" zu machen. Mach nach jeder kleinen Änderung einen schnellen Testlauf.
Führe die Hauptabläufe in dieser Reihenfolge durch:
Wenn ein Schritt fehlschlägt, behebe ihn, bevor du neue Features hinzufügst. Kleine Apps werden chaotisch, wenn Probleme sich stapeln.
Edge‑Cases sind Eingaben, mit denen du nicht gerechnet hast, die aber echte Nutzer machen werden:
Ein häufiger Fix ist, leere Aufgaben zu blockieren:
const text = input.value.trim();
addButton.disabled = text === "";
(Das kannst du bei jedem input‑Event ausführen und nochmal kurz bevor du die Aufgabe hinzufügst.)
Wenn Klicks nichts tun, liegt es meist an einem dieser Punkte:
id oder Klassenname stimmt zwischen HTML und JS nicht überein).app.js nicht gefunden).Wenn etwas seltsam wirkt, logg es:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
Schau in die Entwicklerkonsole deines Browsers nach Fehlern (rote Meldungen). Entferne die Logs, sobald du das Problem behoben hast, damit dein Projekt nicht zugemüllt wird.
Eine To‑Do‑App ist erst „fertig", wenn sie für echte Menschen bequem nutzbar ist — auf dem Handy, per Tastatur und mit Hilfsmitteln wie Screenreadern.
Auf kleinen Bildschirmen sind winzige Knöpfe frustrierend. Gib klickbaren Elementen genug Platz:
Im CSS helfen padding, größere font-size und gap oft am meisten.
Screenreader brauchen klare Namen für Bedienelemente.
\u003clabel\u003e haben (beste Option). Wenn du es nicht sichtbar zeigen willst, kannst du es per CSS verstecken, aber es muss im HTML bleiben.aria-label="Aufgabe löschen" haben, damit Screenreader Kontexte liefern.Das hilft Menschen zu verstehen, was jeder Knopf macht, ohne zu raten.
Stell sicher, dass die gesamte App ohne Maus nutzbar ist:
\u003cform\u003e macht das natürlich möglich).Nutze eine gut lesbare Schriftgröße (16px ist ein guter Start) und starken Kontrast (dunkle Schrift auf hellem Hintergrund oder umgekehrt). Vermeide, Farbe allein zu verwenden, um „erledigt" anzuzeigen — kombiniere z. B. Durchstreichung plus einen optischen Zustand.
Wenn alles funktioniert, nimm dir 10–15 Minuten Zeit zum Aufräumen. Das macht spätere Änderungen leichter und hilft dir, das Projekt beim nächsten Mal schneller zu verstehen.
Klein und vorhersehbar:
/index.html — Seitenstruktur (Eingabe, Knopf, Liste)/styles.css — Aussehen (Abstände, Schrift, „erledigt"‑Stil)/app.js — Verhalten (hinzufügen, umschalten, löschen, speichern/laden)/README.md — kurze Notizen für das „zukünftige Ich"Wenn du Unterordner magst, geht auch:
/css/styles.css/js/app.jsAchte darauf, dass \u003clink\u003e und \u003cscript\u003e Pfade übereinstimmen.
Ein paar schnelle Verbesserungen:
taskInput, taskList, saveTasks()Beispiel, das leichter zu überfliegen ist:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)Dein README.md kann simpel sein:
index.html im Browser)Mindestens: Erstelle eine ZIP‑Kopie, nachdem du einen Meilenstein erreicht hast (z. B. „localStorage funktioniert"). Für Versionierung ist Git ideal, aber optional. Eine einzige Sicherung kann vor versehentlichem Löschen retten.
Veröffentlichen heißt einfach, die Dateien (HTML, CSS, JavaScript) öffentlich online zu stellen, sodass andere einen Link öffnen und die App nutzen können. Da diese To‑Do‑App eine statische Seite ist (läuft im Browser, kein Server nötig), kannst du sie kostenlos hosten.
Kurzfassung:
/) wählen.Wenn du separate Dateien nutzt, prüfe Dateinamen genau (styles.css vs style.css).
Einfacher „upload and go"‑Weg:
localStorage funktioniert).Wenn alles passt, schick den Link an einen Freundin — frische Augen finden oft Probleme schnell.
Du hast eine funktionierende To‑Do‑App gebaut. Wenn du weiterlernen willst, ohne gleich ein riesiges Projekt zu starten, sind diese Erweiterungen sinnvoll und lehren nützliche Muster.
Füge einen „Bearbeiten"‑Knopf neben jede Aufgabe. Beim Klicken tauschst du das Label gegen ein kleines Eingabefeld (mit vorgefülltem Text) und zeigst „Speichern" und „Abbrechen" an.
Tipp: Behalte deine Aufgaben als Array von Objekten (mit id und text). Beim Bearbeiten: finde das richtige Objekt per id, aktualisiere text, re‑rendern und speichern.
Drei Knöpfe oben: Alle, Aktiv, Erledigt.
Speichere den aktuellen Filter z. B. in currentFilter = 'all'. Beim Rendern zeigst du:
Leicht gehalten:
YYYY-MM-DD) neben der Aufgabe anzeigenSchon ein Feld mehr zeigt, wie du Datenmodell und UI zusammen erweiterst.
Der große Schritt: statt in localStorage zu speichern, sendest du Aufgaben an eine API (Server) per fetch(). Der Server legt sie in einer Datenbank ab, sodass Aufgaben über Geräte hinweg synchronisieren.
Wenn du diesen Sprung machen willst, ohne alles neu zu bauen, helfen Prototyping‑Tools und Plattformen, die Backend/Frontend‑Scaffolding generieren.
Baue eine Notizen‑App (mit Suche) oder einen Habit Tracker (tägliche Einträge). Beide nutzen die gleichen Fertigkeiten: Listendarstellung, Bearbeiten, Speichern und einfaches UI‑Design.