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

作るものと学べること
このガイドで「アプリ」と言うと、小さなウェブアプリのことを指します。ブラウザで開く単一ページで、クリックや入力に反応します。インストール不要、アカウント不要、面倒なセットアップなし――ローカルで動かせるシンプルなプロジェクトです。
目標(完成したアプリでできること)
最終的に、次ができる To‑Do アプリを作ります:
- タスクを追加(入力ボックスから)
- タスクを完了にする(完了したものがわかる)
- タスクを削除
- ページをリロードしてもタスクが残る(
localStorageを使います)
エンタープライズ向けに完璧にする必要はありません。これは初心者が基本を学ぶための小さなプロジェクトです。
学ぶこと(わかりやすく)
段階を追って作りながら、フロントエンドの基本を学びます:
- ページ構造を作る HTML(入力、ボタン、リスト)
- 見やすくする CSS
- クリック処理やリストの作成・更新をする JavaScript
- タスクを保存・読み込みする localStorage
- 基本的な テストとデバッグ(小さなミスを見つけて直す)
準備するもの
シンプルに。必要なのは:
- コンピュータ(Windows / Mac / Linux)
- 最新の ブラウザ(Chrome、Firefox、Safari、Edge)
- コードエディタ(VS Code など)
フォルダを作って数ファイル編集できれば準備OKです。
ステップ 1: 目的と機能を決める
コードを書く前に「成功」の定義を決めます。このチュートリアルでは、タスクを管理する小さなアプリを作ります。
アプリの一つの役割を決める
作業中に前に置いておく一文を書きましょう:
「このアプリはタスクをリストに追加して忘れないようにするためのものです。」
カレンダーやリマインダー、タグ、アカウントなどのアイデアは後でにしましょう。
必須機能とあったら良い機能
短く二つのリストを作ります:
必須(このプロジェクトで実装するもの):
- タスクの追加(テキスト入力 → ボタン)
- リストでタスクを見る
- タスクを完了済みにする
- タスクを削除する
- リロード後もタスクを保持する(後で localStorage を使う)
あれば良い(今回は不要): 期限、優先度、カテゴリ、検索、ドラッグ&ドロップ、クラウド同期。
必須を小さく保つと完成しやすくなります。
画面を言葉で描く
単一ページに収まります:
- テキスト入力(「タスクを追加…」)と「Add」ボタン
- 下にタスクのリスト
- 各タスクに「完了」トグルと「削除」ボタン
「完了」の定義を決める
具体的にしておくと迷わない:
- 「完了」=タスクが取り消し線(あるいはフェード)で表示され、削除しない限りリストに残る。
これでファイルを用意する準備が整いました。
ステップ 2: プロジェクトファイルを用意する
コードを書く前に、アプリの「家」をきれいに作ります。ファイルを整理しておくとその後が楽になります。
1) プロジェクトフォルダを作る
todo-app のような名前で新しいフォルダを作ります。中に以下の3つのファイルを作成します:
index.html(ページ構造)styles.css(見た目)app.js(挙動とインタラクション)
拡張子が隠れていると index.html.txt になりがちなので注意してください。
2) エディタとブラウザで開く
フォルダをエディタ(VS Code など)で開き、index.html をブラウザで表示します。ページが空でも大丈夫。次で中身を追加します。
3) 変更を確認する方法
ブラウザは自動で更新されないことが多いです(自動更新ツールを使っていない場合)。基本のループは:
- エディタで保存
- ブラウザのタブに切り替え
- ページをリロードして確認
動かないと感じたらまずはリロードを試してください。
任意:簡易ローカルサーバ(あれば便利)
index.html をダブルクリックして開くだけでも作れますが、ローカルサーバを使うと後々の不具合を避けられます。おすすめ:
- VS Code の Live Server を使って「Go Live」
- Python が入っていればフォルダ内で:
python -m http.server
表示されたアドレス(例: http://localhost:8000)をブラウザで開きます。
ステップ 3: ページ構造を作る(HTML)
ここではアプリの骨組みを作ります。この HTML 自体はまだインタラクティブではありませんが、JavaScript が読み書きする要素を用意します。
必要な最小レイアウト
- ページタイトル
- タスクを入力するボックス
- 追加ボタン(Add)
- 新しいタスクが表示されるリスト領域
わかりやすい 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 でよく使います。
ステップ 4: 見た目を整える(CSS)
今はプレーンな文書に見えるはずです。少し 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 で動きをつけます。
ステップ 5: タスクを追加する(JavaScript の基本)
入力、ボタン、リストがあるので、これらを動かします。目的はシンプル:テキストを入力して Add を押すか Enter を押すと、新しいアイテムがリストに現れること。
ボタンと 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 のセレクタが正しく一致しているか確認してください。これは初心者がよく陥るところです。
ステップ 6: 完了と削除の処理を追加する
追加できるようになったら、タスクを操作できるようにします:完了に切り替えたり、削除したりします。
タスクはオブジェクトで管理する
文字列だけで管理する代わりにオブジェクトにしておくと安定した識別子と状態を持たせられます:
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クラスを付けて完了タスクの見た目を変える。
ステップ 7: タスクを保存して残す(localStorage)
今はページをリロードするとタスクが消えてしまいます。これはタスクが JavaScript のメモリにしか存在しないためです。
localStorage はブラウザ内の小さな保存箱
localStorage はキー名で文字列を保存できる仕組みです。サーバー不要で手軽に使えるため初心者プロジェクトに最適です。
一覧を JSON に変換して保存し、ページロード時に読み戻します。
1) 変更のたびに保存する
タスクを追加・完了切替・削除したら都度 saveTasks() を呼びます。
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
更新後は:
saveTasks(tasks);
renderTasks(tasks);
2) ページ開始時に読み込んでレンダリングする
ページが読み込まれたら保存データを読みます。保存がなければ空配列にフォールバックします。
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
これでリロードしてもタスクが残ります。
ヒント:localStorage は文字列しか保存できないので、JSON.stringify() / JSON.parse() を使います。
ステップ 8: テストとよくあるバグ修正
テストは地味ですが、アプリを確実に動かすために重要です。小さな変更ごとに確認をしましょう。
60秒でできる簡単テスト
基本的な流れを試します:
- タスクを追加する
- 完了にする
- 削除する
- ページをリロードして動作を確認する
どれかで失敗したら、その問題を直してから新機能に進みましょう。
初心者が壊しやすいエッジケース
- とても長いテキスト(表示が崩れないか)
- 同じタスクを複数追加(正しいものが削除されるか)
- 空の入力(空タスクを追加しないか)
空タスクを防ぐには:
const text = input.value.trim();
addButton.disabled = text === "";
(input イベントでチェックし、追加直前にも確認すると堅牢です。)
「何も起きない」時のチェックリスト
- セレクタが一致しているか(id / class のスペルミス)
- スクリプトが読み込まれているか(ファイル名/パス)
- JavaScript のエラーで処理が止まっていないか
コンソールで状態を確認する
不具合の原因がわからないときはログを出して調べます:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
ブラウザのコンソールにエラー(赤い文字)が出ていないか確認し、原因を直したらログは消しましょう。
ステップ 9: 使いやすさを高める(アクセシビリティ + モバイル)
実際のユーザーが使いやすいことが大事です。スマホやキーボード、スクリーンリーダーでも快適に使えるようにしましょう。
モバイルでタップしやすくする
小さすぎるボタンは誤タップの原因です。タップ領域を確保しましょう:
- ボタンのパディングを大きめ(目安 44px 高さ)にする
- Done と Delete の間に余白を入れる
- 入力フィールドはモバイルで幅いっぱいにする
CSS で padding、font-size、gap を調整すると改善しやすいです。
ラベルと ARIA を追加する理由
スクリーンリーダー向けにコントロールの名前を明確にします。
- 入力には
labelを使う(見せたくない場合は視覚的に隠しても HTML に残す) - アイコンだけのボタンには
aria-label="Delete task"のように意味を補う
これで支援技術でも操作しやすくなります。
キーボード操作(Tab と Enter)
マウス無しでも操作できるようにします:
- Tab で入力とボタンに移動できること
- 入力で Enter を押すと追加されること(
<form>を使えば自然に動作します) - タスク追加後にフォーカスを入力に戻すと連続入力が速くなります
読みやすさ:コントラストとフォントサイズ
基本フォントサイズは 16px 程度、濃いテキストと明るい背景などコントラストを強めにすると見やすくなります。色だけで完了を示すのではなく、取り消し線などの明確なスタイルを追加しましょう。
ステップ 10: コードの整理
動くようになったら 10〜15 分ほどかけて整理しましょう。将来の修正が楽になります。
シンプルなフォルダ構成
小さく予測しやすい構成がおすすめです:
/index.html— ページ構造/styles.css— スタイル/app.js— 挙動(追加、完了切替、削除、保存/読み込み)/README.md— 将来の自分へのメモ
サブフォルダが好みなら /css/styles.css、/js/app.js のようにしても構いません。パスを合わせるのを忘れずに。
JavaScript を読みやすくするコツ
- 名前をわかりやすく:
taskInput、taskList、saveTasks()など - 関数は小さく一つの仕事だけにする
- 必要な部分にだけ短いコメントを書く(全部にコメントは不要)
例:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)
簡単な README を追加する
README.md には:
- 何をするアプリか(To‑Do リストでタスクを保存する)
- 実行方法(
index.htmlをブラウザで開く) - 既知の制限(あれば)
バックアップを取る
節目ごとにフォルダを zip するだけでも安心です。バージョン管理が必要なら Git を使いましょう(任意)。
ステップ 11: 公開して共有する
公開とは、HTML/CSS/JS のファイルをインターネット上に置き、誰でも開けるようにすることです。静的サイトなので無料でホスティングできます。
オプション 1: GitHub Pages
大まかな手順:
- GitHub アカウントを作る(未取得なら)
- リポジトリを作りファイルをアップロード(または Git で push)
- リポジトリの Settings → Pages で公開するブランチとフォルダを選ぶ
- 公開 URL が発行されるので開いて確認する
ファイル名のスペルミス(styles.css と style.css など)に注意してください。
オプション 2: Netlify / Vercel(ドラッグ&ドロップで簡単)
- アカウントを作る
- Deploy / Add New Project を選ぶ
- フォルダをドラッグするか GitHub 連携でデプロイ
- 発行された URL を開いて動作を確かめる
共有前の簡単チェックリスト
- ページをリロードしてもタスクが残るか
- 追加、完了、削除が問題なく動くか
- モバイルで崩れないか
クリアしたら友達にリンクを送って試してもらいましょう。第三者の目はバグを見つけるのに有効です。
次に挑戦できる簡単な拡張
動く To‑Do アプリができたら、次は価値を追加しつつ学べる小さな拡張を試してみてください。
1) タスクの編集
各タスクに「Edit」ボタンを追加し、クリックでテキストを編集する小さな入力に切り替えます。保存とキャンセルを用意しましょう。
ポイント:タスクは id と text を持つオブジェクトのままにしておくと編集は簡単です。
2) フィルター(All / Active / Done)
上部に All / Active / Done のボタンを追加し、現在のフィルターを currentFilter = 'all' のような変数で管理します。レンダリング時に表示を切り替えます。
3) 期限やカテゴリー
軽めの拡張:
- 任意の期限(日付文字列)を追加して表示する
- 「Work / Home / School」などのカテゴリーをドロップダウンで選べるようにする
1つ追加するだけでもデータモデルと UI の連携を学べます。
4) いずれは localStorage からバックエンドへ
次の段階では、localStorage の代わりに API に fetch() で送ってサーバー側に保存することでデバイス間で同期できるようになります。
このステップは大きな変更ですが、既存の UI ロジックを再利用できます。
5) おすすめの次の初心者プロジェクト
- 検索付きのノートアプリ
- 毎日のチェックインをする習慣トラッカー
どちらもリストのレンダリング、編集、保存、UI 設計のスキルを活かせます。
よくある質問
このアプリはどのブラウザで動きますか?
- ほとんどの現代的なブラウザ(Chrome、Firefox、Safari、Edge)で動作します。
- JavaScript が有効であることが前提です。
- 特別なサーバーは不要で、ローカルで
index.htmlを開くだけで動きます。
「何も起きない」時はどうデバッグすれば良いですか?
- ブラウザのコンソール(開発者ツール)を開いて、エラーメッセージが出ていないか確認してください。
- HTML の id/class が JavaScript のセレクタと一致しているか確認します(よくある原因です)。
app.jsのファイルパスが正しいか、ページに正しく読み込まれているか確認します。- 追加時に何も起きない場合は、
console.log("Adding:", text);のように一時的にログを出して原因を探します。
よくあるエッジケースにはどんなものがありますか?
- 長いテキスト:CSS でオーバーフロー処理(折り返しや省略)を追加する。
- 同じタスクを複数追加:各タスクに一意の
idを付け、削除/編集時はそのidで対象を特定する。 - 空の入力:追加前に
trim()を使って空文字をブロックする。
これらは小さなチェックで簡単に対処できます。