Un guide clair et pas à pas pour créer une petite application to‑do : planifier les fonctionnalités, créer les écrans, ajouter la logique, sauvegarder les données, tester et publier.

Quand j'écris « application » dans ce guide, je parle d'une petite web app : une page unique que vous ouvrez dans un navigateur et qui réagit à ce que vous cliquez et tapez. Pas d'installation, pas de comptes, pas de grosse configuration — juste un petit projet que vous pouvez lancer localement.
À la fin, vous aurez une application to‑do qui peut :
localStorage (fermer l'onglet ne supprime pas tout)Ce ne sera pas parfait ni « niveau entreprise », et c'est voulu. C'est un projet pour débutants conçu pour enseigner les bases sans vous surcharger d'outils.
Vous construirez l'application pas à pas et assimilerez les éléments de base du fonctionnement des apps front-end :
Restez simple. Il vous faut seulement :
Si vous savez créer un dossier et éditer quelques fichiers, vous êtes prêt.
Avant d'écrire du code, décidez à quoi ressemble la « réussite ». Ce tutoriel construit une petite app avec une tâche claire : vous aider à suivre des tâches à faire.
Écrivez un objectif en une phrase que vous garderez devant vous pendant le développement :
« Cette application me permet d'ajouter des tâches à une liste pour ne pas les oublier. »
C'est tout. Si vous avez envie d'ajouter calendriers, rappels, tags ou comptes, mettez ces idées de côté pour plus tard.
Faites deux listes rapides :
Indispensable (pour ce projet) :
localStorage plus loin)Optionnel (pas requis aujourd'hui) : dates d'échéance, priorités, catégories, recherche, glisser-déposer, synchronisation cloud.
Limiter les éléments « indispensables » vous aide à finir réellement.
Cette app peut tenir sur une seule page avec :
Soyez précis pour ne pas vous bloquer :
Avec cela décidé, vous pouvez préparer les fichiers du projet.
Avant d'écrire du code, créons un petit « chez-soi » pour l'app. Organiser les fichiers dès le départ facilitera les étapes suivantes.
Créez un nouveau dossier sur votre ordinateur et nommez-le par exemple todo-app. Ce dossier contiendra tout pour ce projet.
À l'intérieur, créez trois fichiers :
index.html (la structure de la page)styles.css (l'apparence et la mise en page)app.js (le comportement et l'interactivité)Si votre système masque les extensions, vérifiez bien de créer de vrais fichiers (évitez index.html.txt).
Ouvrez le dossier todo-app dans votre éditeur (VS Code, Sublime Text, etc.). Ensuite ouvrez index.html dans votre navigateur.
La page peut être vide pour l'instant — c'est normal. Nous ajouterons le contenu à l'étape suivante.
Quand vous modifiez les fichiers, le navigateur ne se mettra pas à jour automatiquement (sauf si vous utilisez un outil qui le fait).
La boucle de base est :
Si « rien ne se passe », commencez toujours par actualiser.
Vous pouvez ouvrir index.html directement, mais un serveur local évite des problèmes étranges plus tard (surtout si vous travaillez avec des fichiers ou des données).
Options faciles pour débutants :
python -m http.server
Puis ouvrez l'adresse affichée (souvent http://localhost:8000) dans votre navigateur.
Créons maintenant une ossature propre pour l'app. Ce HTML ne rendra pas l'app interactive (ce sera pour la suite), mais il fournira des points clairs auxquels votre JavaScript pourra accéder.
Nous inclurons :
Gardez des noms simples et lisibles. De bons ID/classes facilitent la suite car votre JavaScript peut récupérer les éléments sans ambiguïté.
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
C'est tout pour la structure. Notez que nous avons utilisé id="taskInput" et id="taskList" — ce sont les éléments que votre JavaScript utilisera le plus.
Actuellement, la page ressemble probablement à un document basique. Un peu de CSS la rendra plus facile à utiliser : espacements, texte lisible et boutons qui ont l'air cliquables.
Un bloc centré garde l'app lisible et évite que le contenu s'étire sur tout l'écran.
/* 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);
}
Chaque tâche doit ressembler à une « ligne » distincte, avec un espacement confortable.
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; }
Quand une tâche est complétée, elle doit changer visuellement pour être identifiable d'un coup d'œil.
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
Gardez les boutons constants pour qu'ils fassent partie de la même application.
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
C'est suffisant pour une interface propre et conviviale — pas de techniques avancées nécessaires. Ensuite, nous relierons le comportement avec JavaScript.
Maintenant que vous avez l'entrée, le bouton et la liste sur la page, faites-les fonctionner. L'objectif : quand quelqu'un tape une tâche et appuie sur Ajouter (ou sur Entrée), un nouvel élément apparaît dans la liste.
Dans votre fichier JavaScript, récupérez d'abord les éléments nécessaires, puis écoutez deux actions : un clic sur le bouton et la touche Entrée dans l'entrée.
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() pour supprimer les espaces superflus.li, lui assigne du texte et l'ajoute à la liste.Si rien ne se passe, vérifiez que les IDs dans le HTML correspondent exactement aux sélecteurs JavaScript (c'est une erreur fréquente).
Maintenant que vous pouvez ajouter des tâches, rendons-les actionnables : on doit pouvoir marquer une tâche comme terminée et la supprimer.
Au lieu d'utiliser des chaînes simples, stockez les tâches sous forme d'objets. Cela donne à chaque tâche une identité stable et un endroit pour suivre l'état « terminé » :
text : le contenu de la tâchedone : true ou falseid : un numéro unique pour retrouver/supprimer la bonne tâcheExemple simple :
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
Quand vous affichez chaque tâche, incluez soit une case à cocher soit un bouton « Terminé », plus un bouton « Supprimer ».
Un listener d'événements réagit aux clics : vous l'attachez à un bouton (ou à la liste entière), et quand l'utilisateur clique, votre code s'exécute.
Un modèle simple pour débutant est la délégation d'événements : mettez un seul écouteur de clic sur le conteneur de la liste, puis vérifiez quel élément a été cliqué.
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);
});
Dans votre fonction renderTasks() :
data-id="${task.id}" à chaque bouton..done).Actuellement, si vous actualisez la page, tout disparaît. C'est parce que les tâches ne vivent que dans la mémoire JavaScript. Au rechargement, cette mémoire est réinitialisée.
localStorage est inclus dans le navigateur. Pensez-y comme une petite boîte où l'on peut ranger du texte sous un nom (une « clé »). C'est parfait pour les projets débutants car il n'y a pas de serveur ni de comptes.
Nous allons stocker la liste complète des tâches en JSON, puis la recharger au démarrage.
Chaque fois que vous ajoutez, marquez ou supprimez une tâche, appelez saveTasks().
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
Là où votre app met à jour le tableau tasks, faites ceci juste après :
saveTasks(tasks);
renderTasks(tasks);
Au chargement de la page, lisez la valeur sauvegardée. S'il n'y a rien, retournez une liste vide.
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
C'est tout : votre app se rappelle maintenant des tâches après actualisation.
Astuce : localStorage ne stocke que des chaînes, donc JSON.stringify() transforme votre tableau en texte, et JSON.parse() le retransforme en tableau lors du chargement.
Les tests peuvent sembler ennuyeux, mais c'est le moyen le plus rapide pour rendre votre app fiable. Faites une petite vérification après chaque changement.
Parcourez le flux principal dans cet ordre :
Si une étape échoue, corrigez avant d'ajouter de nouvelles fonctionnalités. Les petits projets deviennent vite difficiles si on empile les problèmes.
Les cas limites sont des entrées auxquelles vous n'avez pas pensé :
Une correction courante est de bloquer les tâches vides :
const text = input.value.trim();
addButton.disabled = text === "";
(Exécutez ça sur chaque événement input, et de nouveau juste avant d'ajouter.)
Quand les clics ne répondent pas, c'est souvent :
id ou une classe diffère entre HTML et JS).app.js introuvable).Quand un comportement est aléatoire, loggez :
console.log("Adding:", text);
console.log("Current tasks:", tasks);
Regardez la Console du navigateur pour des erreurs (texte rouge). Une fois le problème résolu, retirez les logs pour ne pas encombrer le projet.
Une application n'est vraiment « prête » que si elle est confortable à utiliser — sur téléphone, au clavier et avec des aides comme les lecteurs d'écran.
Sur petit écran, des boutons minuscules sont pénibles. Donnez de l'espace aux éléments cliquables :
Augmenter padding, font-size et gap en CSS aide souvent beaucoup.
Les lecteurs d'écran ont besoin de noms clairs pour les contrôles.
\u003clabel\u003e (c'est la meilleure option). Si vous ne voulez pas l'afficher, vous pouvez le masquer visuellement en CSS, mais laissez-le dans le HTML.aria-label="Delete task" pour que le lecteur d'écran comprenne leur fonction.Cela aide les utilisateurs à comprendre les contrôles sans deviner.
Assurez-vous que l'on peut utiliser toute l'app sans souris :
\u003cform\u003e rend cela naturel).Utilisez une taille de police lisible (16px est une bonne base) et un contraste fort (texte foncé sur fond clair). N'utilisez pas la couleur seule pour indiquer « terminé » — ajoutez une apparence claire comme le barré.
Quand tout fonctionne, prenez 10–15 minutes pour ranger. Cela facilite les corrections futures et vous aide à retrouver votre projet plus tard.
Gardez-la petite et prévisible :
/index.html — la structure de la page/styles.css — l'apparence (espacements, polices, style « terminé »)/app.js — le comportement (ajout, bascule terminé, suppression, sauvegarde/chargement)/README.md — notes pour le « vous du futur »Si vous préférez des sous-dossiers :
/css/styles.css/js/app.jsAssurez-vous que vos balises <link> et <script> pointent vers les bons chemins.
Quelques bonnes pratiques :
taskInput, taskList, saveTasks()Par exemple, il est plus lisible d'avoir :
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)Votre README.md peut être simple :
index.html dans un navigateur)Au minimum, zippez le dossier après une étape importante (par ex. « localStorage fonctionne »). Si vous voulez de l'historique, Git est parfait — mais facultatif. Même une copie de sauvegarde peut vous sauver d'une suppression accidentelle.
Publier signifie mettre les fichiers (HTML, CSS, JavaScript) quelque part en ligne pour que d'autres ouvrent un lien et utilisent l'app. Comme cette to‑do est un « site statique », vous pouvez l'héberger gratuitement.
Étapes générales :
Si l'app utilise plusieurs fichiers, vérifiez l'exactitude des noms (styles.css vs style.css, etc.).
Pour la méthode la plus simple :
localStorage fonctionne).Quand tout est OK, envoyez le lien à un ami : un regard neuf repère souvent les problèmes.
Vous avez une to‑do fonctionnelle. Si vous voulez continuer sans passer à un gros projet, ces améliorations apportent de la valeur et enseignent des notions utiles.
Ajoutez un bouton « Modifier » à côté de chaque tâche. Au clic, remplacez le label par un petit champ prérempli, avec « Enregistrer » et « Annuler ».
Astuce : conservez votre modèle de données comme un tableau d'objets (id et text). Pour modifier : trouvez la tâche par id, mettez à jour text, réaffichez et sauvegardez.
Ajoutez trois boutons : Toutes, Actives, Terminées.
Stockez le filtre courant dans une variable currentFilter = 'all'. Lors du rendu, affichez :
Restez léger :
YYYY-MM-DD) et affichez-la à côté de la tâcheMême un champ supplémentaire vous apprendra à synchroniser modèle de données et UI.
L'idée générale : au lieu d'enregistrer dans localStorage, envoyez les tâches vers une API (serveur) avec fetch(). Le serveur les stockera dans une base de données et permettra la synchronisation entre appareils.
Si vous voulez faire ce saut sans tout réécrire, une plateforme de prototypage peut vous aider à esquisser les endpoints API, les tables et les changements d'UI, puis exporter du code prêt à être étudié ou personnalisé.
Essayez une application de notes (avec recherche) ou un suivi d'habitudes (check‑ins quotidiens). Ils réutilisent les mêmes compétences : rendu de listes, édition, sauvegarde et design d'interface simple.