シンプルなTo‑Doアプリを作るための、わかりやすいステップバイステップガイド:機能を計画し、画面を作り、ロジックを追加し、データを保存し、テストして公開する方法を学びます。

このガイドで「アプリ」と言うと、小さなウェブアプリのことを指します。ブラウザで開く単一ページで、クリックや入力に反応します。インストール不要、アカウント不要、面倒なセットアップなし――ローカルで動かせるシンプルなプロジェクトです。
最終的に、次ができる To‑Do アプリを作ります:
localStorage を使います)エンタープライズ向けに完璧にする必要はありません。これは初心者が基本を学ぶための小さなプロジェクトです。
段階を追って作りながら、フロントエンドの基本を学びます:
シンプルに。必要なのは:
フォルダを作って数ファイル編集できれば準備OKです。
コードを書く前に「成功」の定義を決めます。このチュートリアルでは、タスクを管理する小さなアプリを作ります。
作業中に前に置いておく一文を書きましょう:
「このアプリはタスクをリストに追加して忘れないようにするためのものです。」
カレンダーやリマインダー、タグ、アカウントなどのアイデアは後でにしましょう。
短く二つのリストを作ります:
必須(このプロジェクトで実装するもの):
あれば良い(今回は不要): 期限、優先度、カテゴリ、検索、ドラッグ&ドロップ、クラウド同期。
必須を小さく保つと完成しやすくなります。
単一ページに収まります:
具体的にしておくと迷わない:
これでファイルを用意する準備が整いました。
コードを書く前に、アプリの「家」をきれいに作ります。ファイルを整理しておくとその後が楽になります。
todo-app のような名前で新しいフォルダを作ります。中に以下の3つのファイルを作成します:
index.html(ページ構造)styles.css(見た目)app.js(挙動とインタラクション)拡張子が隠れていると index.html.txt になりがちなので注意してください。
フォルダをエディタ(VS Code など)で開き、index.html をブラウザで表示します。ページが空でも大丈夫。次で中身を追加します。
ブラウザは自動で更新されないことが多いです(自動更新ツールを使っていない場合)。基本のループは:
動かないと感じたらまずはリロードを試してください。
index.html をダブルクリックして開くだけでも作れますが、ローカルサーバを使うと後々の不具合を避けられます。おすすめ:
python -m http.server
表示されたアドレス(例: http://localhost:8000)をブラウザで開きます。
ここではアプリの骨組みを作ります。この HTML 自体はまだインタラクティブではありませんが、JavaScript が読み書きする要素を用意します。
わかりやすい id / class を使うと後で JavaScript が扱いやすくなります。
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>
これで構造は完成です。id="taskInput" と id="taskList" を JavaScript でよく使います。
今はプレーンな文書に見えるはずです。少し CSS を加えるだけで使いやすくなります:余白、読みやすさ、クリックしやすさを改善しましょう。
中央のボックスに収めると視線が安定します。
/* 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);
}
各タスクを別の行に見せ、余白を取ります。
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; }
完了したタスクは一目でわかるようにします。
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
ボタンのサイズや角を統一して、アプリ全体の一体感を出します。
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
ここまでで十分に読みやすい UI になります。次は JavaScript で動きをつけます。
入力、ボタン、リストがあるので、これらを動かします。目的はシンプル:テキストを入力して Add を押すか Enter を押すと、新しいアイテムがリストに現れること。
JavaScript では必要な要素を取得し、ボタンクリックと入力内の Enter キーに対して処理を登録します。
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() で前後の余白を取り除きます。li を作ってテキストをセットし、リストに追加します。動かない場合は、HTML の id と JavaScript のセレクタが正しく一致しているか確認してください。これは初心者がよく陥るところです。
追加できるようになったら、タスクを操作できるようにします:完了に切り替えたり、削除したりします。
文字列だけで管理する代わりにオブジェクトにしておくと安定した識別子と状態を持たせられます:
text: タスクの内容done: 真偽値で完了状態id: 一意の番号で対象を特定例:
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
各タスクをレンダリングするときにチェックボックスや「Done」ボタン、そして「Delete」ボタンを追加します。
イベントリスナーはクリックに反応する仕組みです。ボタンやリスト全体にリスナーをつけてクリック時に処理を行います。
初心者に優しいパターンは イベント委譲(event delegation):タスクリストのコンテナに一つだけクリックリスナーをつけ、クリックされた要素を判定する方法です。
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);
});
renderTasks() 内では:
data-id="${task.id}" を付ける。.done クラスを付けて完了タスクの見た目を変える。今はページをリロードするとタスクが消えてしまいます。これはタスクが JavaScript のメモリにしか存在しないためです。
localStorage はキー名で文字列を保存できる仕組みです。サーバー不要で手軽に使えるため初心者プロジェクトに最適です。
一覧を JSON に変換して保存し、ページロード時に読み戻します。
タスクを追加・完了切替・削除したら都度 saveTasks() を呼びます。
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
更新後は:
saveTasks(tasks);
renderTasks(tasks);
ページが読み込まれたら保存データを読みます。保存がなければ空配列にフォールバックします。
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
これでリロードしてもタスクが残ります。
ヒント:localStorage は文字列しか保存できないので、JSON.stringify() / JSON.parse() を使います。
テストは地味ですが、アプリを確実に動かすために重要です。小さな変更ごとに確認をしましょう。
基本的な流れを試します:
どれかで失敗したら、その問題を直してから新機能に進みましょう。
空タスクを防ぐには:
const text = input.value.trim();
addButton.disabled = text === "";
(input イベントでチェックし、追加直前にも確認すると堅牢です。)
不具合の原因がわからないときはログを出して調べます:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
ブラウザのコンソールにエラー(赤い文字)が出ていないか確認し、原因を直したらログは消しましょう。
実際のユーザーが使いやすいことが大事です。スマホやキーボード、スクリーンリーダーでも快適に使えるようにしましょう。
小さすぎるボタンは誤タップの原因です。タップ領域を確保しましょう:
CSS で padding、font-size、gap を調整すると改善しやすいです。
スクリーンリーダー向けにコントロールの名前を明確にします。
label を使う(見せたくない場合は視覚的に隠しても HTML に残す)aria-label="Delete task" のように意味を補うこれで支援技術でも操作しやすくなります。
マウス無しでも操作できるようにします:
<form> を使えば自然に動作します)基本フォントサイズは 16px 程度、濃いテキストと明るい背景などコントラストを強めにすると見やすくなります。色だけで完了を示すのではなく、取り消し線などの明確なスタイルを追加しましょう。
動くようになったら 10〜15 分ほどかけて整理しましょう。将来の修正が楽になります。
小さく予測しやすい構成がおすすめです:
/index.html — ページ構造/styles.css — スタイル/app.js — 挙動(追加、完了切替、削除、保存/読み込み)/README.md — 将来の自分へのメモサブフォルダが好みなら /css/styles.css、/js/app.js のようにしても構いません。パスを合わせるのを忘れずに。
taskInput、taskList、saveTasks() など例:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)README.md には:
index.html をブラウザで開く)節目ごとにフォルダを zip するだけでも安心です。バージョン管理が必要なら Git を使いましょう(任意)。
公開とは、HTML/CSS/JS のファイルをインターネット上に置き、誰でも開けるようにすることです。静的サイトなので無料でホスティングできます。
大まかな手順:
ファイル名のスペルミス(styles.css と style.css など)に注意してください。
クリアしたら友達にリンクを送って試してもらいましょう。第三者の目はバグを見つけるのに有効です。
動く To‑Do アプリができたら、次は価値を追加しつつ学べる小さな拡張を試してみてください。
各タスクに「Edit」ボタンを追加し、クリックでテキストを編集する小さな入力に切り替えます。保存とキャンセルを用意しましょう。
ポイント:タスクは id と text を持つオブジェクトのままにしておくと編集は簡単です。
上部に All / Active / Done のボタンを追加し、現在のフィルターを currentFilter = 'all' のような変数で管理します。レンダリング時に表示を切り替えます。
軽めの拡張:
1つ追加するだけでもデータモデルと UI の連携を学べます。
次の段階では、localStorage の代わりに API に fetch() で送ってサーバー側に保存することでデバイス間で同期できるようになります。
このステップは大きな変更ですが、既存の UI ロジックを再利用できます。
どちらもリストのレンダリング、編集、保存、UI 設計のスキルを活かせます。
index.html を開くだけで動きます。app.js のファイルパスが正しいか、ページに正しく読み込まれているか確認します。console.log("Adding:", text); のように一時的にログを出して原因を探します。id を付け、削除/編集時はその id で対象を特定する。trim() を使って空文字をブロックする。これらは小さなチェックで簡単に対処できます。