つくる ひとことメモウィジェット
このレッスンで分かること
- 配列に貯めたデータを画面に描き、追加と削除で貯め方を変える形が書けます
- 描き直す関数を 1 本に決めておくと、追加も削除も同じ道を通ります
- 変わったところで保存し、読み込み時に読み戻すと、メモがリロードをまたいで残ります
つくる ひとことメモウィジェット とは
この章までの全部を 1 か所に集める総合制作です。入力して追加、一覧に表示、削除ボタンで消す、そして保存。プロフィールサイトに小さなメモ帳を足します。
どの章の何を使うか
このレッスンは新しい文法をほとんど足しません。代わりに、ここまで別々に練習してきたものを 1 つの機能に合流させます。手が止まったら、下の表から該当する章に戻ってください。
| 章 | 使うもの | メモ帳のどこで効くか |
|---|---|---|
| 第5章 | 配列と push と splice | メモを貯める入れ物と、足す・抜く操作 |
| 第6章 | 関数とコールバック | 描き直す関数、追加する関数、forEach に渡す関数 |
| 第7章 | オブジェクト | 1 件のメモを { text: "..." } の形で持つ |
| 第8章 | createElement と appendChild | li と span と button を組み立てて一覧に置く |
| 第9章 | addEventListener と input の値 | 追加ボタンと削除ボタン、入力欄の読み取り |
| 第10章 | localStorage と JSON | メモの配列を文字列にして保存し、読み戻す |
いちばん大事な考え方は、画面ではなく配列を本物として扱うことです。追加も削除も、まず配列を書き換え、そのあと配列の中身をもとに一覧をまるごと描き直します。画面の側を直接いじって帳尻を合わせようとすると、件数の表示や削除ボタンの番号がすぐにずれます。
データを本物として持ち、画面はそれを写したものにする。この考え方はデータ駆動と呼ばれ、React のようなライブラリの土台にもなっています。入門の最後にこの形を手で書いておくと、次に進んだときの理解がまるで変わります。
描き直す関数を 1 本に決める
まず、いまの配列から一覧を作り直す renderMemos を書きます。最初に textContent を空にして中身を捨て、forEach で 1 件ずつ組み立て直します。
function renderMemos() {
memoList.textContent = "";
memos.forEach(function (memo, index) {
const item = document.createElement("li");
item.className = "memo-item";
const text = document.createElement("span");
text.className = "memo-text";
text.textContent = memo.text;
const removeButton = document.createElement("button");
removeButton.className = "memo-delete";
removeButton.type = "button";
removeButton.textContent = "削除";
removeButton.addEventListener("click", function () {
memos.splice(index, 1);
saveMemos();
renderMemos();
});
item.appendChild(text);
item.appendChild(removeButton);
memoList.appendChild(item);
});
memoCount.textContent = memos.length + " 件";
}forEach の第 2 引数 index が、そのメモが配列の何番目かです。削除ボタンの中でその番号を使い、splice(index, 1) で 1 件だけ抜きます。関数の中から外側の index を見られるのは、第6章でやった通り、関数が書かれた場所の変数を覚えているからです。
削除したあとに renderMemos() をもう一度呼んでいるのを見落とさないでください。描き直すと番号は振り直され、残ったメモのボタンは正しい番号を持ち直します。1 件ずつ画面から要素を取り除く書き方にすると、この番号がずれて、2 件目を消したつもりが 3 件目が消える、という不具合になります。
追加する側
追加は、空のときを先に弾いてから配列に足します。第6章の早期 return です。
function addMemo() {
const value = memoInput.value.trim();
if (value === "") {
memoMessage.className = "form-message error";
memoMessage.textContent = "メモを入力してください";
return;
}
memos.push({ text: value });
saveMemos();
memoInput.value = "";
memoMessage.textContent = "";
renderMemos();
}trim() は前後の空白を落とすメソッドです。これが無いと、スペースだけのメモが作れてしまいます。追加できたら memoInput.value を空にして、次のメモをすぐ打てるようにします。細かいですが、これがあるかないかで使い心地がはっきり変わります。
保存を挟む
保存は前のレッスンと同じ形です。配列はそのままでは入らないので JSON.stringify を通し、読むときに JSON.parse で戻します。読めなかったときは空の配列にしておけば、以降の処理は「まだ 1 件も無い」ときと同じに書けます。
function loadMemos() {
try {
const saved = localStorage.getItem("memos");
if (saved === null) return [];
const parsed = JSON.parse(saved);
return Array.isArray(parsed) ? parsed : [];
} catch (error) {
return [];
}
}
function saveMemos() {
try {
localStorage.setItem("memos", JSON.stringify(memos));
} catch (error) {
console.log("この環境では保存できません");
}
}
const memos = loadMemos();Array.isArray で確かめているのは、保存された文字列が壊れていたり、別の形が入っていたときの用心です。memos を const で受けているのは、中身を push や splice で変えることはあっても、入れ物そのものを別の配列に取り替えないからです。第7章でやった const と参照の話がそのまま当てはまります。
呼ぶ順番は、読み出してから描く、です。const memos = loadMemos(); を先に置き、最後に renderMemos(); を 1 回呼べば、前回のメモが並んだ状態でページが始まります。
よくある間違い
保存を書き忘れる場所があるという間違いが最も多いです。追加のときだけ保存して削除で忘れると、消したはずのメモがリロードで復活します。
// 間違い 配列は変わったのに保存していない
memos.splice(index, 1);
renderMemos();
// 正しい 変えたら保存する
memos.splice(index, 1);
saveMemos();
renderMemos();描き直す前に中身を空にし忘れるのも定番です。
// 間違い 呼ぶたびに古い一覧の下へ足されていく
function renderMemos() {
memos.forEach(function (memo) { /* 追加していく */ });
}
// 正しい まっさらにしてから組み立てる
function renderMemos() {
memoList.textContent = "";
memos.forEach(function (memo) { /* 追加していく */ });
}splice(index) と第 2 引数を省くのも危ないです。この書き方だと、その位置から後ろが全部消えます。1 件だけ抜きたいときは splice(index, 1) と必ず 2 つ書いてください。
課題
- 作品とお問い合わせのあいだにメモの section を足す。入力欄 memo-input、追加ボタン memo-add、案内文 memo-message、一覧 memo-list、件数 memo-count を id で用意する
- メモの配列を持ち、loadMemos で localStorage から読み戻す。読めないときや保存が無いときは空の配列にする
- renderMemos 関数で一覧を作り直す。1 件ごとに li と削除ボタンを作り、memo-count に件数を入れる。削除ボタンは splice で 1 件だけ抜く
- 追加ボタンを押したら入力欄の値を trim して配列に足し、入力欄を空にして描き直す。空のときは追加せず memo-message に入力をうながす文を出す
- 追加と削除のどちらでも、配列を変えたあとに JSON.stringify で保存する