Een duidelijke, stap-voor-stap handleiding om een kleine to‑do-app te bouwen: plan features, maak schermen, voeg logica toe, sla data op, test en publiceer.

Als ik het in deze handleiding over een “app” heb, bedoel ik een kleine webapp: één pagina die je in een browser opent en die reageert op wat je klikt en typt. Geen installatie, geen accounts, geen zware setup—gewoon een simpel project dat je lokaal kunt draaien.
Aan het einde heb je een to‑do-app die kan:
localStorage (zodat het sluiten van het tabblad niet alles wist)Hij wordt niet perfect of “enterprise-grade”, en dat is precies de bedoeling. Dit is een beginnersproject om de basis te leren zonder veel tools te gebruiken.
Je bouwt de app stap voor stap en leert de kernstukken van hoe front-end webapps werken:
Hou het simpel. Je hebt alleen nodig:
Als je een map kunt maken en een paar bestanden kunt bewerken, ben je klaar.
Voordat je code schrijft, bedenk wat “succes” is. Deze tutorial bouwt één kleine app met één duidelijke taak: helpen taken bij te houden.
Schrijf een doelzin die je tijdens het bouwen voor ogen houdt:
“Deze app laat me taken aan een lijst toevoegen zodat ik ze niet vergeet.”
Dat is alles. Als je de neiging voelt om kalenders, herinneringen, tags of accounts toe te voegen, zet die ideeën opzij voor later.
Maak twee korte lijstjes:
Must-have (voor dit project):
Nice-to-have (niet vereist vandaag): vervaldatums, prioriteiten, categorieën, zoeken, drag-and-drop, cloud sync.
Het klein houden van “must-have” helpt je daadwerkelijk klaar te zijn.
Deze app kan één pagina zijn met:
Wees specifiek zodat je niet vastloopt:
Met dat duidelijk, kun je de projectbestanden opzetten.
Voordat we code schrijven, maken we een klein, schoon “thuis” voor de app. Goede organisatie vanaf het begin maakt de volgende stappen soepeler.
Maak een nieuwe map op je computer en noem die bijvoorbeeld todo-app. Deze map bevat alles voor dit project.
In die map maak je drie bestanden aan:
index.html (de pagina-structuur)styles.css (het uiterlijk en de layout)app.js (het gedrag en de interactiviteit)Als je computer bestandsextensies verbergt (zoals “.html”), zorg dan dat je echte bestanden maakt. Een veelgemaakte beginnersfout is eindigen met index.html.txt.
Open de todo-app-map in je code-editor (VS Code, Sublime Text, etc.). Open daarna index.html in je webbrowser.
Op dit moment kan je pagina leeg zijn—dat is prima. We voegen inhoud toe in de volgende stap.
Als je bestanden wijzigt, ververst de browser niet automatisch (tenzij je een tool gebruikt die dat doet).
Dus de basisloop is:
Als iets “niet werkt”, is refresh het eerste wat je moet proberen.
Je kunt deze app bouwen door op index.html te dubbelklikken, maar een lokale server voorkomt soms rare problemen (vooral als je later data of bestanden laadt).
Beginnervriendelijke opties:
python -m http.server
Open daarna het adres dat het print (vaak http://localhost:8000) in je browser.
Nu maken we een schoon skelet voor de app. Deze HTML maakt nog niets interactief (dat komt later), maar geeft je JavaScript duidelijke plekken om vanaf te lezen en naar te schrijven.
We voegen toe:
Houd namen simpel en leesbaar. Goede IDs/classes maken latere stappen makkelijker omdat je JavaScript elementen makkelijk kan selecteren.
index.html<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>To‑Do App</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main class="app" id="app">
<h1 class="app__title" id="appTitle">My To‑Do List</h1>
<form class="task-form" id="taskForm">
<label class="task-form__label" for="taskInput">New task</label>
<div class="task-form__row">
<input
id="taskInput"
class="task-form__input"
type="text"
placeholder="e.g., Buy milk"
autocomplete="off"
/>
<button id="addButton" class="task-form__button" type="submit">
Add
</button>
</div>
</form>
<ul class="task-list" id="taskList" aria-label="Task list"></ul>
</main>
<script src="app.js"></script>
</body>
</html>
Dat is het voor de structuur. Let op dat we id="taskInput" en id="taskList" gebruiken—dat zijn de twee elementen waar je JavaScript het meest mee werkt.
Op dit moment bestaat je pagina, maar ziet hij er waarschijnlijk uit als een standaard document. Een beetje CSS maakt het makkelijker in gebruik: betere ruimte, leesbare tekst en knoppen die aanvoelen alsof je erop kunt klikken.
Een gecentreerd blok houdt de app gefocust en voorkomt dat inhoud over een brede schermbreedte rekt.
/* 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);
}
Elke taak moet eruitzien als een aparte “rij”, met comfortabele afstand.
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; }
Wanneer een taak voltooid is, moet dat visueel veranderen zodat je het snel ziet.
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
Houd knoppen dezelfde maat en stijl zodat ze aanvoelen als één app.
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
Dat is genoeg styling voor een schoon, vriendelijk UI—geen geavanceerde trucjes nodig. Volgende stap: gedrag toevoegen met JavaScript.
Nu je het invoerveld, de knop en de lijst op de pagina hebt, ga je ze werkend maken. Het doel is simpel: wanneer iemand een taak typt en op Add klikt (of op Enter drukt), verschijnt er een nieuw item in de lijst.
In je JavaScript-bestand pak je eerst de elementen die je nodig hebt, en luister je naar twee acties: een knopklik en de Enter-toets in het invoerveld.
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() om extra spaties aan het begin/einde te verwijderen.<li>, zet de tekst en voegt het toe aan de lijst.Als er niets gebeurt, controleer dan of de element-IDs in de HTML exact overeenkomen met de JavaScript-selectors (dit is een van de meest voorkomende beginnersproblemen).
Nu je taken kunt toevoegen, laten we ze actie geven: je moet een taak als voltooid kunnen markeren en verwijderen.
In plaats van taken als platte tekst op te slaan, gebruik je objecten. Dat geeft elke taak een stabiele identiteit en een plek om de “done”-status bij te houden:
text: wat de taak zegtdone: true of falseid: een uniek nummer zodat we de juiste taak kunnen vinden/verwijderenHier is een eenvoudig voorbeeld:
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
Wanneer je elke taak op de pagina rendert, voeg dan een checkbox of een “Done”-knop toe, plus een “Delete”-knop.
Een event listener is gewoon “een manier om op klikken te reageren.” Je plakt hem op een knop (of de hele lijst), en wanneer de gebruiker klikt, draait je code.
Een handige beginnerspatroon is event delegation: zet één click-listener op de container van de takenlijst en kijk vervolgens wat er precies is aangeklikt.
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 je renderTasks()-functie:
data-id="${task.id}" toe aan elke knop..done-klasse toe te voegen).Op dit moment heeft je to‑do-lijst een vervelend probleem: als je de pagina ververst (of het tabblad sluit), verdwijnt alles.
Dat gebeurt omdat je taken alleen in het JavaScript-geheugen bestaan. Bij herladen wordt dat geheugen gereset.
localStorage zit ingebouwd in de browser. Zie het als een klein doosje waar je tekst onder een sleutel kunt bewaren. Het is perfect voor beginnersprojecten omdat je geen server of accounts nodig hebt.
We slaan de hele takenlijst op als JSON-tekst en laden die terug wanneer de pagina opent.
Telkens als je een taak toevoegt, markeert of verwijdert, roep je saveTasks() aan.
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
Waar je app ook het tasks-array bijwerkt, doe dit direct daarna:
saveTasks(tasks);
renderTasks(tasks);
Als de pagina laadt, lees je de opgeslagen waarde. Als er nog niets is opgeslagen, gebruik je een lege lijst.
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
Dat is alles: je app onthoudt nu taken na verversen.
Tip: localStorage slaat alleen strings op, dus JSON.stringify() zet je array om in tekst en JSON.parse() zet het terug naar een echt array als je het laadt.
Testen klinkt saai, maar het is de snelste manier om je to‑do-app van “werkt op mijn machine” naar “werkt altijd” te krijgen. Doe een korte testronde na elke kleine wijziging.
Loop de hoofdflow door in deze volgorde:
Als een stap faalt, los dat op voordat je nieuwe features toevoegt. Kleine apps worden rommelig als je problemen opstapelt.
Edge-cases zijn invoer die je niet ontworpen hebt, maar die echte mensen toch doen:
Een veelgebruikte oplossing is lege taken blokkeren:
const text = input.value.trim();
addButton.disabled = text === "";
(Draaien bij elke input-event en nogmaals vlak voordat je toevoegt is handig.)
Wanneer klikken niets doen, is het meestal een van deze:
id of class verschilt tussen HTML en JS).app.js niet gevonden).Als iets willekeurig lijkt, log het:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
Check de Console in de browser voor fouten (rode tekst). Als je het probleem hebt opgelost, verwijder de logs zodat ze je project niet volspammen.
Een to‑do-app is pas “af” wanneer hij comfortabel is voor echte mensen—op telefoons, met een toetsenbord en met hulpmiddelen zoals screenreaders.
Op kleine schermen zijn kleine knoppen frustrerend. Geef klikbare dingen genoeg ruimte:
Als je CSS gebruikt, helpt het vergroten van padding, font-size en gap vaak het meest.
Screenreaders hebben duidelijke namen voor bedieningselementen nodig.
<label> hebben (dat is het beste). Als je het niet zichtbaar wilt tonen, kun je het visueel verbergen met CSS, maar laat het in de HTML staan.aria-label="Delete task" hebben zodat de screenreader niet alleen “button” aankondigt zonder context.Dit helpt mensen te begrijpen wat elk element doet zonder te hoeven raden.
Zorg dat je de hele app kunt gebruiken zonder muis:
<form> zodat Enter natuurlijk werkt).Gebruik een leesbare basislettergrootte (16px is een goede uitgang) en duidelijk kleurcontrast (donkere tekst op een lichte achtergrond of andersom). Gebruik niet alleen kleur om “done” aan te geven—voeg een duidelijke stijl toe zoals een doorgestreepte tekst naast een visuele staat.
Nu alles werkt, neem 10–15 minuten om op te schonen. Dat maakt toekomstige fixes makkelijker en helpt je project te begrijpen als je later terugkomt.
Houd het klein en voorspelbaar:
/index.html — de pagina-structuur (invoerveld, knop, lijst)/styles.css — hoe de app eruitziet (spacing, fonts, “done”-stijl)/app.js — het gedrag (toevoegen, toggle done, verwijderen, opslaan/laden)/README.md — korte aantekeningen voor “toekomstige jij”Als je submappen verkiest, kun je ook:
/css/styles.css/js/app.jsZorg er gewoon voor dat je link- en script-paden overeenkomen.
Een paar snelle tips:
taskInput, taskList, saveTasks()Bijvoorbeeld is het makkelijker te scannen:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)Je README.md kan simpel zijn:
index.html in een browser)Minimaal: zip de map nadat je een mijlpaal hebt bereikt (bijv. “localStorage werkt”). Voor versiegeschiedenis is Git geweldig—maar optioneel. Zelfs één backup kan je redden van per ongeluk wissen.
Publiceren betekent gewoon dat je de bestanden (HTML, CSS, JavaScript) ergens openbaar zet zodat anderen een link kunnen openen en de app gebruiken. Omdat deze to‑do-app een “static site” is (hij draait in de browser en heeft geen server nodig), kun je hem gratis hosten op verschillende diensten.
Hoog-niveau stappen:
Als je app losse bestanden gebruikt, controleer dan of bestandsnamen exact overeenkomen met je links (bijv. styles.css vs style.css).
Als je de eenvoudigste “upload en klaar” manier wilt:
localStorage werkt).Als het goed werkt, stuur de link naar een vriend en vraag of hij hem probeert—verse ogen vinden vaak problemen snel.
Je hebt een werkende to‑do-app gebouwd. Als je wil blijven leren zonder meteen naar een groot project te springen, voegen deze upgrades echte waarde toe en leren ze nuttige patronen.
Voeg een “Edit”-knop toe naast elke taak. Als je erop klikt, vervang je het label door een klein invoerveld (vooraf ingevuld), plus “Save” en “Cancel.”
Tip: houd je taakdata als een array van objecten (met id en text). Bewerken wordt dan: vind de taak op id, werk text bij, render opnieuw en sla op.
Voeg drie knoppen toe bovenaan: All, Active, Done.
Bewaar de huidige filter in een variabele zoals currentFilter = 'all'. Bij renderen toon je:
Houd het lichtgewicht:
YYYY-MM-DD) en toon die naast de taakZelfs één extra veld leert je hoe je je datamodel en UI samen bijwerkt.
Wanneer je eraan toe bent, is het grote idee: in plaats van opslaan in localStorage, stuur je taken naar een API (een server) met fetch(). De server bewaart ze in een database, zodat taken over apparaten synchroniseren.
Als je die sprong wilt maken zonder alles opnieuw te bouwen, kan een prototyping-platform zoals Koder.ai je helpen het “volgende niveau” snel te schetsen: beschrijf functies in chat (API-endpoints, databasetabellen, UI-wijzigingen), werk iteratief in planning mode, en exporteer de broncode wanneer je klaar bent om te leren van (of te aanpassen aan) het gegenereerde React/Go/PostgreSQL-project.
Probeer een notes-app (met zoeken) of een habit tracker (dagelijkse check-ins). Ze hergebruiken dezelfde vaardigheden: lijst-rendering, bewerken, opslaan en eenvoudige UI-design.