Una guida chiara e passo dopo passo per creare una piccola app to‑do: pianifica le funzionalità, crea le schermate, aggiungi la logica, salva i dati, testa e pubblica.

Quando parlo di “app” in questa guida, intendo una piccola web app: una singola pagina che apri in un browser e che risponde a quello che clicchi o scrivi. Nessuna installazione, nessun account, nessuna configurazione pesante—solo un progetto semplice che puoi eseguire in locale.
Alla fine avrai una app to‑do che può:
localStorage (quindi chiudere la scheda non cancella tutto)Non sarà perfetta o “enterprise-grade”, ed è voluto. Questo è un progetto per principianti pensato per insegnare le basi senza aggiungere troppi strumenti.
Costruirai l'app passo dopo passo e imparerai i pezzi principali di come funzionano le web app front-end:
Mantieni tutto semplice. Ti serve solo:
Se sai creare una cartella e modificare qualche file, sei pronto.
Prima di scrivere codice, decidi cosa significa “successo”. Questo tutorial costruisce una piccola app con un unico scopo chiaro: aiutarti a tenere traccia delle attività.
Scrivi un obiettivo in una frase che puoi tenere davanti a te mentre costruisci:
“Questa app mi permette di aggiungere attività a una lista così non le dimentico.”
Questo è tutto. Se senti la tentazione di aggiungere calendari, promemoria, tag o account, metti quelle idee da parte per dopo.
Fai due liste rapide:
Obbligatorio (per questo progetto):
Opzionale (non richiesto oggi): date di scadenza, priorità, categorie, ricerca, drag-and-drop, sincronizzazione cloud.
Mantenere gli “obbligatori” ridotti ti aiuta a finire davvero.
Questa app può essere una singola pagina con:
Sii specifico così non ti blocchi:
Con questo deciso, sei pronto per impostare i file del progetto.
Prima di scrivere codice, creiamo un piccolo “spazio” ordinato per l'app. Tenere i file organizzati dall'inizio rende più semplici i passaggi successivi.
Crea una nuova cartella sul tuo computer e chiamala ad esempio todo-app. Questa cartella conterrà tutto del progetto.
Dentro quella cartella crea tre file:
index.html (la struttura della pagina)styles.css (l'aspetto e il layout)app.js (il comportamento e l'interattività)Se il tuo sistema nasconde le estensioni, assicurati di creare file reali: un errore comune è ritrovarsi con index.html.txt.
Apri la cartella todo-app nel tuo editor di codice (VS Code, Sublime Text, ecc.). Poi apri index.html nel browser.
A questo punto la pagina potrebbe essere vuota—va bene. Aggiungeremo contenuto nel passaggio successivo.
Quando modifichi i file, il browser non si aggiorna automaticamente (a meno che tu non usi uno strumento che lo fa).
Quindi il ciclo base è:
Se qualcosa “non funziona”, prova a ricaricare: è la prima cosa da tentare.
Puoi aprire il progetto facendo doppio click su index.html, ma un server locale evita problemi strani più avanti (soprattutto quando inizi a salvare dati o caricare file).
Opzioni amichevoli per principianti:
python -m http.server
Poi apri l'indirizzo che stampa (spesso http://localhost:8000) nel browser.
Ora creiamo uno scheletro pulito per l'app. Questo HTML non la rende ancora interattiva (lo faremo dopo), ma dà al JavaScript punti chiari da leggere e aggiornare.
Includeremo:
Usa nomi semplici e leggibili. ID/class chiari rendono più facile far prendere al tuo JavaScript gli elementi senza confusione.
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
Questo è tutto per la struttura. Nota che abbiamo usato id=\"taskInput\" e id=\"taskList\"—sono gli elementi con cui parlerai più spesso in JavaScript.
Ora la pagina esiste, ma probabilmente sembra un documento semplice. Un po' di CSS la rende più facile da usare: spaziatura chiara, testo leggibile e pulsanti che sembrano cliccabili.
Una box centrata mantiene l'app focalizzata e impedisce che il contenuto si allarghi su schermi grandi.
/* 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);
}
Ogni attività dovrebbe sembrare una “riga” separata, con spaziatura comoda.
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; }
Quando un'attività è completata, dovrebbe cambiare visivamente così la riconosci subito.
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
Mantieni i pulsanti della stessa dimensione e stile così sembrano parte della stessa app.
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
Questo è sufficiente per un'interfaccia pulita e amichevole—niente trucchi avanzati. Poi collegheremo il comportamento con JavaScript.
Ora che hai input, bottone e lista sulla pagina, farai in modo che reagiscano. L'obiettivo è semplice: quando qualcuno scrive un'attività e preme Aggiungi (o Enter), appare un elemento nella lista.
Nel tuo file JavaScript, prima prendi gli elementi necessari, poi ascolta due azioni: il clic sul bottone e il tasto Enter dentro l'input.
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() per togliere spazi superflui agli estremi.\u003cli\u003e, imposta il suo testo e lo aggiunge alla lista.Se non succede nulla, ricontrolla che gli ID negli HTML coincidano esattamente con i selettori usati in JavaScript (è uno degli errori più comuni per i principianti).
Ora che puoi aggiungere attività, rendiamole azionabili: dovresti poter segnare un'attività come fatta e rimuoverla.
Invece di usare solo stringhe, conserva le attività come oggetti. Questo dà a ogni attività un'identità stabile e un posto dove tenere lo stato “fatto”:
text: cosa dice l'attivitàdone: true o falseid: un numero unico così possiamo trovare/eliminare l'attività giustaEsempio semplice:
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
Quando renderizzi ogni attività sulla pagina, includi una checkbox o un pulsante “Fatto”, più un pulsante “Elimina”.
Un event listener è semplicemente “un modo per reagire ai clic”. Lo attacchi a un bottone (o all'intera lista) e quando l'utente clicca il codice viene eseguito.
Un pattern comodo per principianti è la delegazione degli eventi: metti un listener di clic sul contenitore della lista e poi controlli cosa è stato cliccato.
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);
});
Nella tua funzione renderTasks():
data-id="${task.id}" a ogni pulsante..done).Adesso la tua lista ha un problema: se ricarichi la pagina (o chiudi la scheda), tutto sparisce.
Succede perché le attività esistono solo nella memoria di JavaScript. Al reload quella memoria si azzera.
localStorage è integrato nel browser. Pensalo come una scatola dove puoi conservare testo sotto un nome (una “chiave”). È perfetto per progetti per principianti perché non serve un server o account.
Salveremo l'intera lista delle attività come testo JSON, poi la caricheremo quando la pagina apre.
Ogni volta che aggiungi, segnali come fatto o cancelli, chiama saveTasks().
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
Dove il tuo codice aggiorna l'array tasks, esegui subito:
saveTasks(tasks);
renderTasks(tasks);
Quando la pagina si carica, leggi il valore salvato. Se non c'è nulla, usa una lista vuota.
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
Questo è tutto: ora l'app ricorda le attività tra i refresh.
Suggerimento: localStorage salva solo stringhe, quindi JSON.stringify() trasforma il tuo array in testo e JSON.parse() lo riporta in un array quando lo carichi.
Testare può sembrare noioso, ma è il modo più veloce per trasformare la tua app da “funziona sul mio computer” a “funziona sempre”. Fai un rapido controllo dopo ogni piccola modifica.
Esegui il flusso principale in quest'ordine:
Se un passaggio fallisce, correggilo prima di aggiungere nuove funzionalità. I piccoli progetti diventano subito disordinati se accumuli problemi.
I casi limite sono input per cui non hai progettato, ma che la gente fa comunque:
Una soluzione comune è bloccare le attività vuote:
const text = input.value.trim();
addButton.disabled = text === "";
(Esegui questo su ogni evento input, e di nuovo subito prima di aggiungere.)
Quando i clic non funzionano, in genere è una di queste cause:
id o nome di classe è diverso tra HTML e JS).app.js non trovato).Quando qualcosa sembra casuale, logga valori:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
Controlla la Console del browser per errori (testo rosso). Una volta risolto, rimuovi i log così non intasano il progetto.
Una app to‑do è veramente “completa” quando è comoda per persone reali—su telefoni, con la tastiera e con strumenti assistivi come screen reader.
Su schermi piccoli, i pulsanti troppo piccoli sono fastidiosi. Dai agli elementi cliccabili spazio sufficiente:
Se usi CSS, aumentare padding, font-size e gap spesso è la differenza principale.
Gli screen reader hanno bisogno di nomi chiari per i controlli.
\u003clabel\u003e (la soluzione migliore). Se non vuoi mostrarla, puoi nasconderla visivamente via CSS ma tenerla nell'HTML.aria-label="Delete task" così lo screen reader non li annuncia come “bottone” senza contesto.Questo aiuta le persone a capire cosa fa ogni controllo senza indovinare.
Assicurati che sia possibile usare tutta l'app senza il mouse:
\u003cform\u003e fa sì che Enter funzioni naturalmente).Usa una dimensione leggibile (16px è un buon riferimento) e contrasto forte (testo scuro su sfondo chiaro o viceversa). Evita di usare solo il colore per indicare “fatto”—aggiungi uno stile chiaro come la barratura oltre allo stato “Done”.
Ora che tutto funziona, dedica 10–15 minuti a mettere in ordine. Questo rende le correzioni future più facili e ti aiuta a ricordare come è fatto il progetto quando torni.
Mantienila piccola e prevedibile:
/index.html — la struttura della pagina (input, bottone, lista)/styles.css — l'aspetto (spaziatura, font, stile “fatto”)/app.js — il comportamento (aggiungi, toggle, elimina, salva/carica)/README.md — note veloci per il “te futuro”Se preferisci sottocartelle, puoi anche:
/css/styles.css/js/app.jsAssicurati solo che i percorsi in \u003clink\u003e e \u003cscript\u003e corrispondano.
Qualche miglioramento semplice:
taskInput, taskList, saveTasks()Ad esempio è più facile leggere:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)Il tuo README.md può essere semplice:
index.html nel browser)Al minimo, comprimi la cartella dopo aver raggiunto una milestone (per esempio “localStorage funziona”). Se vuoi storico delle versioni, Git è ottimo—ma opzionale. Anche solo una copia di backup può salvarti da cancellazioni accidentali.
Pubblicare significa mettere i file dell'app (HTML, CSS, JavaScript) da qualche parte pubblica su internet così altri possono aprire un link e usarla. Poiché questa to‑do app è un “sito statico” (gira nel browser e non ha bisogno di server), puoi ospitarla gratuitamente su diversi servizi.
Passaggi generali:
Se l'app usa file separati, ricontrolla i nomi dei file e che corrispondano esattamente ai link (ad esempio styles.css vs style.css).
Se vuoi l'approccio più semplice “drag-and-drop”:
localStorage funzionante).Se passa, invia il link a un amico e chiedigli di provarlo—occhi freschi trovano problemi velocemente.
Hai costruito una to‑do app funzionante. Se vuoi continuare a imparare senza saltare a un progetto enorme, questi upgrade aggiungono valore reale e insegnano concetti utili.
Aggiungi un pulsante “Modifica” accanto a ogni attività. Al clic, sostituisci l'etichetta con un piccolo campo di input (pre-riempito), più “Salva” e “Annulla”.
Suggerimento: conserva i dati come array di oggetti (con id e text). Modificare diventa: trova l'attività per id, aggiorna text, ri-renderizza e salva.
Aggiungi tre pulsanti in alto: Tutte, Attive, Fatte.
Salva il filtro corrente in una variabile come currentFilter = 'all'. Quando renderizzi, mostra:
Mantieni leggero il tutto:
YYYY-MM-DD) e mostrala accanto all'attivitàAnche un solo campo in più insegna come aggiornare il modello dati e l'interfaccia insieme.
Quando sei pronto, l'idea principale è: invece di salvare su localStorage, invii le attività a un'API usando fetch(). Il server le conserva in un database, così le attività si sincronizzano tra dispositivi.
Se vuoi provare questo salto senza ricostruire tutto, una piattaforma di prototipazione come Koder.ai può aiutarti a generare rapidamente la versione “next”: descrivi le API, il database e le modifiche UI nella chat, iteri in planning mode e poi esporti il codice quando sei pronto per imparare dal progetto generato (React/Go/PostgreSQL, per esempio).
Prova a costruire una notes app (con ricerca) o un habit tracker (check-in giornalieri). Riutilizzano le stesse abilità: renderizzare liste, modificare elementi, salvare e progettare una UI semplice.