メモ帳アプリ 1日目のゴール
1日目のテーマは 「ブラウザ上で動く、シンプルなメモ帳アプリの“最初の形”を作る」 ことです。
重点として挙がっている
querySelector()/querySelectorAll()addEventListener()createElement()/append()/remove()classListdatasetlocalStorage
を使いながら、今日はここまでを目指します。
- 画面に「メモ入力欄」と「追加ボタン」と「メモ一覧」を作る
- メモを追加すると、一覧に表示される
- メモを削除できる
- メモ一覧を
localStorageに保存する(ページを閉じても残る)
ToDoアプリで学んだ DOM 操作と localStorage を、 「メモ帳」という別の題材でもう一度体に入れていくイメージです。
1日目の全体ステップ
1日目の流れはこんな感じです。
- メモ帳アプリの画面レイアウトを決める(HTML)
- JavaScriptで DOM 要素を
querySelector()でつかまえる - メモ一覧を管理する配列を用意する
- メモを追加する関数を作る
- メモ一覧を画面に描画する関数を作る
- メモを削除するボタンを作る
localStorageに保存・読み込みする関数を作る- ページ読み込み時に保存データを読み込んで表示する
メモ帳アプリの画面レイアウト(HTML)
まずは、シンプルなメモ帳の画面を作ります。
- 上に「タイトル」
- その下に「メモ入力欄」と「追加ボタン」
- さらに下に「メモ一覧」
という構成です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>メモ帳アプリ 1日目</title>
<style>
/* メモの本文を少し見やすくするためのスタイル */
.memo-item {
border: 1px solid #ccc;
padding: 8px;
margin-bottom: 8px;
}
.memo-item-title {
font-weight: bold;
margin-bottom: 4px;
}
.memo-item-body {
white-space: pre-wrap; /* 改行をそのまま表示する */
}
</style>
</head>
<body>
<h1>メモ帳アプリ(1日目)</h1>
<div>
<label for="memoTitleInput">タイトル:</label>
<input id="memoTitleInput" type="text" placeholder="メモのタイトルを入力してください">
</div>
<div>
<label for="memoBodyInput">本文:</label><br>
<textarea id="memoBodyInput" rows="4" cols="40" placeholder="メモの本文を入力してください"></textarea>
</div>
<div>
<button id="addMemoButton">メモを追加</button>
</div>
<p id="errorMessage" style="color: red;"></p>
<h2>メモ一覧</h2>
<div id="memoList"></div>
<script>
// ここに JavaScript を書いていきます
</script>
</body>
</html>
ポイント:
- タイトル用に
input、本文用にtextareaを用意 - メモ一覧は
<div id="memoList"></div>に、 JavaScriptからcreateElement()/append()で追加していく
アプリの状態と定数を定義する
次に、JavaScript側で「メモ一覧」を管理する配列と、 localStorage 用のキー名を定義します。
// --- アプリの状態(State) ---
// メモ一覧(1件ごとにオブジェクトを持つ)
let memos = [];
// 次に使うメモのID(1, 2, 3, ... と増えていく)
let nextMemoId = 1;
// localStorageで使うキー名(このアプリ専用の保存箱の名前)
const STORAGE_KEY = "my_memo_app_data";
JavaScript深掘りポイント:
- メモを「オブジェクト」で表現します。 例えば、javascript
{ id: 1, title: "買い物リスト", body: "牛乳\nパン\n卵", createdAt: 1710000000000 } nextMemoIdを持っておくことで、 追加するたびにユニークなIDを振ることができます。STORAGE_KEYはlocalStorageの「このアプリ専用の名前」です。
DOM要素を querySelector / querySelectorAll で取得する
画面上の要素を JavaScript から操作できるようにします。
// --- DOM参照(画面上の要素をつかまえる) ---
const memoTitleInput = document.querySelector("#memoTitleInput");
const memoBodyInput = document.querySelector("#memoBodyInput");
const addMemoButton = document.querySelector("#addMemoButton");
const errorMessage = document.querySelector("#errorMessage");
const memoList = document.querySelector("#memoList");
JavaScriptここでは querySelector() だけですが、 後で「タグ付きメモ」などに発展させるときに querySelectorAll() が活きてきます。
localStorage に保存・読み込みする関数
保存関数(saveToStorage)
// --- ストレージ:状態をlocalStorageに保存する ---
// memos配列とnextMemoIdをまとめて保存する
function saveToStorage() {
// 保存したいデータを1つのオブジェクトにまとめる
const data = {
memos: memos,
nextMemoId: nextMemoId
};
// オブジェクトをJSON文字列に変換する
const json = JSON.stringify(data);
// localStorageに文字列として保存する
localStorage.setItem(STORAGE_KEY, json);
}
JavaScript深掘りポイント:
localStorageは「文字列しか保存できない」- 配列やオブジェクトを保存したいときは
JSON.stringify()で文字列に変換する memosとnextMemoIdを一緒に保存しておくことで、 ページを再読み込みしてもIDがかぶらない
読み込み関数(loadFromStorage)
// --- ストレージ:localStorageから状態を読み込む ---
function loadFromStorage() {
// localStorageからJSON文字列を取り出す
const json = localStorage.getItem(STORAGE_KEY);
// まだ何も保存されていない場合は何もしない
if (json === null) {
return;
}
try {
// JSON文字列をオブジェクトに変換する
const data = JSON.parse(json);
// ざっくり形をチェックしてから代入する
if (Array.isArray(data.memos) && typeof data.nextMemoId === "number") {
memos = data.memos;
nextMemoId = data.nextMemoId;
}
} catch (error) {
// 保存データが壊れていた場合は無視する
console.error("保存データの読み込みに失敗しました:", error);
}
}
JavaScriptメモを追加する関数(addMemo)
次に、メモを1件追加する関数を作ります。
// --- 操作ロジック:メモを追加する ---
function addMemo(title, body) {
const newMemo = {
id: nextMemoId,
title: title,
body: body,
createdAt: Date.now() // 追加した時刻を記録しておく
};
// 次に使うIDを1つ進める
nextMemoId = nextMemoId + 1;
// 新しいメモを配列に追加
memos.push(newMemo);
// 状態が変わったら保存
saveToStorage();
}
JavaScriptポイント:
- メモは「タイトル」と「本文」を持つ
createdAtを持たせておくと、 後で「新しい順」「古い順」などの並び替えに使える
メモ一覧を画面に描画する関数(renderMemos)
ここが DOM 操作のメインです。 配列 memos をもとに、画面上にメモを表示します。
// --- 描画:状態から画面(DOM)を作り直す ---
function renderMemos() {
// 一覧をいったん空にする
memoList.innerHTML = "";
// memos配列の1件ごとにDOMを作る
memos.forEach(function(memo) {
// メモ全体を包むdiv
const memoDiv = document.createElement("div");
memoDiv.classList.add("memo-item");
// このメモがどれかを識別するためのIDをdata-idに持たせる
memoDiv.dataset.id = String(memo.id);
// タイトル部分
const titleDiv = document.createElement("div");
titleDiv.classList.add("memo-item-title");
titleDiv.textContent = memo.title;
// 本文部分
const bodyDiv = document.createElement("div");
bodyDiv.classList.add("memo-item-body");
bodyDiv.textContent = memo.body;
// 削除ボタン
const deleteButton = document.createElement("button");
deleteButton.textContent = "削除";
// 削除ボタンが押されたとき:このメモを削除する
deleteButton.addEventListener("click", function(event) {
// クリックされたボタンから親のmemoDivを探す
const memoElement = event.target.closest(".memo-item");
// memoDivのdata-idからIDを取り出す
const idText = memoElement.dataset.id;
const id = Number(idText);
// 状態を削除して再描画
deleteMemo(id);
});
// memoDivの中に要素を追加する
memoDiv.append(titleDiv);
memoDiv.append(bodyDiv);
memoDiv.append(deleteButton);
// memoListの中にmemoDivを追加する
memoList.append(memoDiv);
});
}
JavaScript深掘りポイント:
createElement()で新しい要素を作り、append()で親要素に追加していくclassList.add()で CSS クラスを付けることで、 見た目を整えつつ「意味」も持たせられるdataset.idにIDを持たせておくことで、 「どのメモを削除するか」を簡単に特定できる
メモを削除する関数(deleteMemo)
削除ボタンから呼ばれる関数です。
// --- 操作ロジック:メモを削除する ---
function deleteMemo(id) {
// 指定したID以外のメモだけを残した新しい配列を作る
const newMemos = memos.filter(function(memo) {
return memo.id !== id;
});
// 元の配列の中身を入れ替える(参照を保ちつつ差し替え)
memos.length = 0;
newMemos.forEach(function(memo) {
memos.push(memo);
});
// 状態が変わったら保存
saveToStorage();
// 新しい状態で画面を作り直す
renderMemos();
}
JavaScript「メモを追加」ボタンのイベント設定
ユーザーの操作とロジックをつなぎます。
// --- イベント設定:メモを追加ボタン ---
addMemoButton.addEventListener("click", function() {
const title = memoTitleInput.value;
const body = memoBodyInput.value;
// エラーメッセージを一度クリア
errorMessage.textContent = "";
// タイトルと本文の両方が空ならエラー
if (title.trim() === "" && body.trim() === "") {
errorMessage.textContent = "タイトルか本文のどちらかは入力してください。";
return;
}
// 状態を更新
addMemo(title, body);
// 入力欄を空にする
memoTitleInput.value = "";
memoBodyInput.value = "";
// 新しい状態で画面を作り直す
renderMemos();
});
JavaScriptポイント:
- 「何も入力されていないメモ」は追加しないようにチェック
- エラーメッセージは
<p id="errorMessage">に表示
ページ読み込み時の初期処理
最後に、ページが読み込まれたときの流れを整えます。
// --- 初期処理 ---
// 1. 保存されている状態を読み込む
loadFromStorage();
// 2. 読み込んだ状態を画面に反映する
renderMemos();
JavaScript1日目の完成コード(JavaScript全体)
HTML の <script> タグの中身として、 ここまでのコードをひとつにまとめるとこうなります。
<script>
// --- アプリの状態(State) ---
let memos = [];
let nextMemoId = 1;
const STORAGE_KEY = "my_memo_app_data";
// --- DOM参照 ---
const memoTitleInput = document.querySelector("#memoTitleInput");
const memoBodyInput = document.querySelector("#memoBodyInput");
const addMemoButton = document.querySelector("#addMemoButton");
const errorMessage = document.querySelector("#errorMessage");
const memoList = document.querySelector("#memoList");
// --- ストレージ:保存 ---
function saveToStorage() {
const data = {
memos: memos,
nextMemoId: nextMemoId
};
const json = JSON.stringify(data);
localStorage.setItem(STORAGE_KEY, json);
}
// --- ストレージ:読み込み ---
function loadFromStorage() {
const json = localStorage.getItem(STORAGE_KEY);
if (json === null) {
return;
}
try {
const data = JSON.parse(json);
if (Array.isArray(data.memos) && typeof data.nextMemoId === "number") {
memos = data.memos;
nextMemoId = data.nextMemoId;
}
} catch (error) {
console.error("保存データの読み込みに失敗しました:", error);
}
}
// --- 描画 ---
function renderMemos() {
memoList.innerHTML = "";
memos.forEach(function(memo) {
const memoDiv = document.createElement("div");
memoDiv.classList.add("memo-item");
memoDiv.dataset.id = String(memo.id);
const titleDiv = document.createElement("div");
titleDiv.classList.add("memo-item-title");
titleDiv.textContent = memo.title;
const bodyDiv = document.createElement("div");
bodyDiv.classList.add("memo-item-body");
bodyDiv.textContent = memo.body;
const deleteButton = document.createElement("button");
deleteButton.textContent = "削除";
deleteButton.addEventListener("click", function(event) {
const memoElement = event.target.closest(".memo-item");
const idText = memoElement.dataset.id;
const id = Number(idText);
deleteMemo(id);
});
memoDiv.append(titleDiv);
memoDiv.append(bodyDiv);
memoDiv.append(deleteButton);
memoList.append(memoDiv);
});
}
// --- 操作ロジック:メモ追加 ---
function addMemo(title, body) {
const newMemo = {
id: nextMemoId,
title: title,
body: body,
createdAt: Date.now()
};
nextMemoId = nextMemoId + 1;
memos.push(newMemo);
saveToStorage();
}
// --- 操作ロジック:メモ削除 ---
function deleteMemo(id) {
const newMemos = memos.filter(function(memo) {
return memo.id !== id;
});
memos.length = 0;
newMemos.forEach(function(memo) {
memos.push(memo);
});
saveToStorage();
renderMemos();
}
// --- イベント設定:メモ追加ボタン ---
addMemoButton.addEventListener("click", function() {
const title = memoTitleInput.value;
const body = memoBodyInput.value;
errorMessage.textContent = "";
if (title.trim() === "" && body.trim() === "") {
errorMessage.textContent = "タイトルか本文のどちらかは入力してください。";
return;
}
addMemo(title, body);
memoTitleInput.value = "";
memoBodyInput.value = "";
renderMemos();
});
// --- 初期処理 ---
loadFromStorage();
renderMemos();
</script>
1日目で本当に掴んでほしいこと
- DOM(画面の要素)と配列(状態)を結びつけると、ブラウザ上で「自分のデータ」を扱えるようになる
querySelector()/addEventListener()/createElement()/append()/remove()/classList/datasetは、 メモ帳のようなアプリでも自然に使える道具になっているlocalStorageとJSON.stringify()/JSON.parse()を組み合わせると、 ページを閉じても消えない「自分専用のメモ帳」が作れる
次のステップでは、
- メモの編集機能
- 検索機能
- タグや色分け
などに広げていくことで、 「自分の生活に本当に役立つメモ帳アプリ」に育てていけます。

