En tydlig steg‑för‑steg‑guide för att bygga en liten att‑göra‑app: planera funktioner, skapa skärmar, lägg till logik, spara data, testa och publicera.

När jag säger “app” i den här guiden menar jag en liten webbapp: en enda sida du öppnar i en webbläsare som reagerar på vad du klickar och skriver. Inga installationer, inga konton, ingen tung setup—bara ett enkelt projekt du kan köra lokalt.
I slutändan kommer du ha en att‑göra‑app som kan:
localStorage (så att stängning av fliken inte raderar allt)Den kommer inte vara perfekt eller ”enterprise‑grade”, och det är poängen. Detta är ett nybörjarprojekt som lär grunderna utan att kasta in en massa verktyg.
Du bygger appen steg för steg och plockar upp de grundläggande delarna i hur front‑end‑webbappar fungerar:
Håll det enkelt. Du behöver bara:
Om du kan skapa en mapp och redigera några filer är du redo.
Innan du skriver någon kod, bestäm vad ”framgång” betyder. Den här handledningen bygger en liten app med en tydlig uppgift: hjälper dig hålla koll på uppgifter du vill göra.
Skriv en mening som mål du kan ha framför dig medan du bygger:
“Den här appen låter mig lägga till uppgifter i en lista så jag inte glömmer dem.”
Det är allt. Om du känner dig frestad att lägga till kalendrar, påminnelser, taggar eller konton—parkera de idéerna till senare.
Gör två snabba listor:
Måste‑ha (för det här projektet):
localStorage senare)Trevligt att ha (inte nödvändigt idag): förfallodatum, prioriteringar, kategorier, sök, dra‑och‑släpp, molnsynk.
Att hålla ”måste‑ha” litet hjälper dig faktiskt bli klar.
Den här appen kan vara en enda sida med:
Var specifik så du inte fastnar:
Med det bestämt är du redo att sätta upp projektfilerna.
Innan vi skriver kod, skapa ett rent litet ”hem” för appen. Att hålla filerna organiserade från start gör nästa steg smidigare.
Gör en ny mapp på din dator och kalla den t.ex. todo-app. Denna mapp kommer innehålla allt för projektet.
Skapa tre filer i mappen:
index.html (sidstrukturen)styles.css (utseende och layout)app.js (beteende och interaktivitet)Om din dator gömmer filändelser, se till att du verkligen skapar riktiga filer. Ett vanligt nybörjarfel är att få index.html.txt istället för index.html.
Öppna todo-app i din kodredigerare (VS Code, Sublime Text, etc.). Öppna sedan index.html i din webbläsare.
Just nu kan sidan vara tom—det är okej. Vi lägger till innehåll i nästa steg.
När du redigerar filer kommer inte webbläsaren automatiskt uppdateras (om du inte använder ett verktyg för det).
Den grundläggande loopen är:
Om något ”inte fungerar”, prova uppdatera som första steg.
Du kan bygga appen genom att dubbelklicka index.html, men en lokal server kan förhindra konstiga problem senare (särskilt när du börjar spara data eller ladda filer).
Nybörjarvänliga alternativ:
python -m http.server
Öppna sedan adressen den skriver ut (ofta http://localhost:8000) i din webbläsare.
Nu skapar vi ett rent skelett för appen. Den här HTML:en gör ingenting interaktivt än (det kommer nästa), men den ger din JavaScript tydliga platser att läsa från och skriva till.
Vi inkluderar:
Håll namn enkla och läsbara. Bra id/klass‑namn gör senare steg lättare eftersom din JavaScript kan hämta element utan förvirring.
index.html\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
Det var allt för strukturen. Notera att vi använde id=\"taskInput\" och id=\"taskList\"—det är de två element din JavaScript kommer interagera mest med.
Just nu finns sidan men den ser förmodligen ut som ett vanligt dokument. Lite CSS gör det enklare att använda: tydligare avstånd, läsbar text och knappar som känns klickbara.
En centrerad ruta håller appen fokuserad och hindrar innehållet från att sträcka sig över en bred skärm.
/* 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);
}
Varje uppgift bör se ut som en separat ”rad” med bekvämt avstånd.
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; }
När en uppgift är slutförd bör den visuellt ändras så du ser det direkt.
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
Håll knappar lika i storlek och stil så de känns som en del av samma app.
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
Det räcker för ett rent, vänligt UI—inga avancerade tricks behövs. Nästa steg är att koppla beteende med JavaScript.
Nu när du har input, knapp och lista på sidan ska vi få dem att göra något. Målet är enkelt: när någon skriver en uppgift och klickar Lägg till (eller trycker Enter) ska ett nytt objekt visas i listan.
I din JavaScript‑fil, hämta först de element du behöver, sedan lyssna efter två händelser: knappklick och Enter i inputen.
const taskInput = document.querySelector('#taskInput');
const addBtn = document.querySelector('#addBtn');
const taskList = document.querySelector('#taskList');
function addTask() {
const text = taskInput.value.trim();
// Blockera tomma uppgifter (inklusive bara mellanslag)
if (text === '') return;
const li = document.createElement('li');
li.textContent = text;
taskList.appendChild(li);
// Rensa input och sätt fokus tillbaka
taskInput.value = '';
taskInput.focus();
}
addBtn.addEventListener('click', addTask);
taskInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
addTask();
}
});
trim() för att ta bort extra mellanslag i början/slutet.li, sätter dess text och lägger till den i listan.Om inget händer, kontrollera att dina id:n i HTML matchar dina JavaScript‑selektorer exakt (detta är en av de vanligaste nybörjarfallen).
Nu när du kan lägga till uppgifter, gör dem handlingsbara: du ska kunna markera en uppgift som klar och ta bort den.
Istället för att spara uppgifter som rena strängar, spara dem som objekt. Det ger varje uppgift en stabil identitet och en plats för ”done”‑status:
text: vad uppgiften sägerdone: true eller falseid: ett unikt nummer så vi kan hitta/radera rätt uppgiftHär är ett enkelt exempel:
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
När du renderar varje uppgift på sidan, inkludera antingen en kryssruta eller en ”Klar”‑knapp, plus en ”Ta bort”‑knapp.
En event listener är bara ett sätt att reagera på klick. Du fäster den på en knapp (eller hela listan), och när användaren klickar körs din kod.
Ett nybörjarvänligt mönster är event delegation: sätt en klick‑lyssnare på listans behållare, och kolla vad som klickades.
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);
});
I din renderTasks()‑funktion:
data-id="${task.id}" på varje knapp..done‑klass).Just nu har din att‑göra‑lista ett irriterande problem: om du uppdaterar sidan (eller stänger fliken) försvinner allt.
Det händer eftersom uppgifterna bara lever i JavaScript‑minnet. När sidan laddas om återställs det minnet.
localStorage är inbyggt i webbläsaren. Tänk på det som en liten låda där du kan lagra text under ett namn (en ”key”). Det är perfekt för nybörjarprojekt eftersom det inte behöver någon server eller konton.
Vi sparar hela uppgiftslistan som JSON‑text och laddar tillbaka den när sidan öppnas.
När du lägger till, markerar eller tar bort en uppgift, kalla saveTasks().
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
Varhelst din app uppdaterar tasks‑arrayen, gör detta direkt efter:
saveTasks(tasks);
renderTasks(tasks);
När sidan laddas, läs det sparade värdet. Om inget finns sparat än, använd en tom lista.
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
Det var allt: din app kommer nu ihåg uppgifter över uppdateringar.
Tips: localStorage lagrar bara strängar, så JSON.stringify() gör om din array till text, och JSON.parse() gör den tillbaka till en riktig array när du laddar.
Testning låter tråkigt, men det är det snabbaste sättet att göra din att‑göra‑app från ”fungerar på min maskin” till ”fungerar alltid”. Kör en snabb genomgång efter varje liten ändring.
Gå igenom huvudflödet i denna ordning:
Om något steg misslyckas, fixa det innan du lägger till nya funktioner. Små appar blir röriga när du staplar problem.
Kantfall är inmatningar du inte designade för, men som riktiga användare ändå gör:
En vanlig fix är att blockera tomma uppgifter:
const text = input.value.trim();
addButton.disabled = text === "";
(Kör det på varje input‑händelse, och igen precis innan du lägger till.)
När klick inte gör något är det oftast en av dessa orsaker:
id eller klassnamn skiljer sig mellan HTML och JS).app.js hittas inte).När något känns slumpmässigt, logga det:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
Kolla webbläsarens Console för fel (röd text). När du har fixat, ta bort loggarna så de inte skräpar ner ditt projekt.
En att‑göra‑app är först ”klar” när den är bekväm för riktiga människor att använda—på telefoner, med tangentbord och med hjälptekniker som skärmläsare.
På små skärmar är små knappar frustrerande. Ge klickbara saker tillräckligt med utrymme:
I CSS hjälper ofta att öka padding, font-size och gap mest.
Skärmläsare behöver tydliga namn på kontroller.
\u003clabel\u003e (bäst). Om du inte vill visa den visuellt, kan du dölja den med CSS men behåll den i HTML.aria-label="Delete task" så skärmläsaren inte annonserar den som en ”knapp” utan kontext.Detta hjälper människor att förstå vad varje kontroll gör utan att gissa.
Se till att du kan använda hela appen utan mus:
\u003cform\u003e så Enter fungerar naturligt).Använd en läsbar basstorlek (16px är en bra utgångspunkt) och stark kontrast (mörk text på ljus bakgrund eller tvärtom). Undvik att använda färg ensam för att visa ”klart”—lägg till en tydlig stil som överstrykning plus en ”klar”‑status.
Nu när allt fungerar, ta 10–15 minuter att snygga till. Det gör framtida fixar enklare och hjälper dig förstå projektet när du kommer tillbaka senare.
Håll det litet och förutsägbart:
/index.html — sidstrukturen (input, knapp, lista)/styles.css — hur appen ser ut (avstånd, fonter, ”klar”‑stil)/app.js — beteendet (lägg till, växla klar, ta bort, spara/ladda)/README.md — snabba anteckningar för ”framtida du”Om du föredrar undermappar kan du göra:
/css/styles.css/js/app.jsSe bara till att dina \u003clink\u003e och \u003cscript\u003e‑sökvägar matchar.
Några snabba vinster:
taskInput, taskList, saveTasks()Till exempel är det lättare att läsa:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)Din README.md kan vara enkel:
index.html i en webbläsare)Minst zipa mappen efter du klarat en milstolpe (t.ex. ”localStorage fungerar”). Om du vill ha versionshistorik är Git utmärkt—men frivilligt. Även en enda backup kan rädda dig från oavsiktlig borttagning.
Publicera betyder att lägga dina filer (HTML, CSS, JavaScript) någonstans offentligt på internet så andra kan öppna en länk och använda den. Eftersom denna att‑göra‑app är en ”statisk site” (den körs i webbläsaren och behöver ingen server) kan du hosta den gratis på flera tjänster.
Högnivåsteg:
Om din app använder separata filer, dubbelkolla filnamnen så de matchar länkarna exakt (t.ex. styles.css vs style.css).
Vill du ha den enklaste ”ladda upp och kör”‑metoden:
localStorage fungerar).När det går igenom, skicka länken till en vän och be dem testa—fräscha ögon hittar problem snabbt.
Du har byggt en fungerande att‑göra‑app. Om du vill fortsätta lära utan att hoppa till ett enormt projekt, ger dessa uppgraderingar verkligt värde och lär nyttiga mönster.
Lägg till en ”Redigera”‑knapp bredvid varje uppgift. När den klickas, byt ut etiketten mot ett litet inputfält (fylld i förval) plus ”Spara” och ”Avbryt”.
Tips: behåll din uppgiftsdata som en array av objekt (med id och text). Redigering blir då: hitta rätt uppgift via id, uppdatera text, rendera om och spara.
Lägg tre knappar överst: Alla, Aktiva, Klara.
Spara aktuell filter i en variabel som currentFilter = 'all'. Vid render: visa:
Håll det lättviktigt:
YYYY-MM-DD) och visa det bredvid uppgiftenBara ett extra fält lär dig hur du uppdaterar datamodellen och UI tillsammans.
När du är redo är stora idén: istället för att spara i localStorage, skickar du uppgifter till ett API (en server) med fetch(). Servern sparar dem i en databas, så uppgifterna synkas över enheter.
Om du vill prova det utan att bygga om allt från scratch kan en prototyp‑plattform som Koder.ai hjälpa dig att snabbt skapa en nästa version: beskriv funktionerna i chatten (API‑endpoints, databas‑tabeller, UI‑ändringar), iterera i planeringsläge och exportera källkoden när du är redo att lära av eller anpassa den genererade React/Go/PostgreSQL‑lösningen.
Bygg en anteckningsapp (med sök) eller en habit tracker (dagliga avprickningar). De återanvänder samma färdigheter: lista, redigering, spara och enkel UI‑design.