見出し画像

【実践】メモ帳アプリを作りながら学ぶDOM操作・localStorage

はじめに

「JavaScriptの基本文法は勉強したけれど、実際に何を作ればいいか分からない。」

そんな方に向けて、本記事ではメモ帳アプリをゼロから実装していきます。

完成するアプリには、以下のような実際のWebアプリでもよく使われる機能を実装します。

  • メモの作成・保存

  • メモの一覧表示

  • メモの編集

  • メモの削除

  • ブラウザを再読み込みしてもデータが消えない保存機能(localStorage)

ただコードを書くだけではなく、

  • 「なぜこのコードを書くのか」

  • 「この関数は何をしているのか」

  • 「この処理はどのような流れで動いているのか」

といった考え方も、できるだけ分かりやすく解説します。

また、本記事はフェーズ・ステップ形式で進めるため、一度に大量のコードを読む必要はありません。

一つひとつ機能を追加しながら進めることで、「動くアプリを作る楽しさ」と「自分で実装できる力」の両方を身につけられる構成になっています。

完成後には、JavaScriptでWebアプリを作るための基礎が身に付き、次のステップとしてToDoアプリや家計簿アプリなど、より実践的なアプリ開発へスムーズに進めるようになります。


この実装で学べること

JavaScript

  • イベント処理(`addEventListener()`)

  • 配列を使ったデータ管理(`push()`・`splice()`)

  • 条件分岐と繰り返し処理(`if`・`forEach()`)

DOM操作

  • HTML要素の取得・更新

  • `innerHTML`を使った一覧表示

  • フォーム入力値の取得・変更

ブラウザAPI

  • `localStorage`への保存・読み込み

  • `JSON.stringify()`・`JSON.parse()`によるデータ変換

Webアプリ開発の考え方

  • データと画面表示を分けて管理する設計

  • CRUD(Create・Read・Update・Delete)の基本実装


対象読者

このような方におすすめ

  • JavaScriptの基礎を学び終え、初めてWebアプリを作る方

  • DOM操作や`localStorage`を実践的に学びたい方

  • ポートフォリオにも使える成果物を作りたい方

このような方には向いていません

  • HTML・CSSをまだ学習していない方

  • JavaScriptの基本文法(変数・関数・配列など)が分からない方

  • ReactやVueなどのフレームワークで開発したい方


必要なもの

本記事では、以下の環境を使用します。

必須

  • Visual Studio Code

  • Google ChromeなどのWebブラウザ

  • HTML・CSS・JavaScriptが実行できる環境

推奨

  • Visual Studio Codeの「Live Server」拡張機能

Live Serverを利用すると、ファイルを保存するたびにブラウザが自動更新されるため、実装内容をすぐに確認できます。

前提知識

本記事では、以下の内容を理解していることを前提としています。

  • HTMLの基本的なタグ

  • CSSの基本的な書き方

  • JavaScriptの基本文法

    • 変数

    • 配列

    • 関数

    • if文

    • forEach()


全体ロードマップ

Phase1 開発環境

  • Step1 フォルダを作る

  • Step2 HTMLを作る

  • Step3 CSSを作る

  • Step4 JavaScriptを読み込む確認

Phase2 メモ一覧を表示する

  • Step1 メモ一覧を表示する場所を作る

  • Step2 メモを画面に表示する

Phase3 メモ追加機能

  • Step1 メモデータを配列で管理する

  • Step2 配列のデータを画面へ表示する

Phase4 localStorage機能

  • Step1 メモデータをブラウザに保存する

  • Step2 保存したメモを読み込む

Phase5 編集機能

  • Step1 編集ボタンを追加する

  • Step2 編集ボタンにクリック処理を追加する

  • Step3 編集内容を入力欄へ反映する

  • Step4 編集内容を保存して更新する

Phase6 削除機能

  • Step1 削除ボタンを追加する

  • Step2 削除ボタンにクリック処理を追加する

Phase7 リファクタリング

  • Step1 空のメモを保存できないようにする

  • Step2 Enterキーでも保存できるようにする

Phase1 開発環境

Step1 フォルダを作る

まずは作業用フォルダを作る。

`~memo-app`

中に3つのファイルを作成する。

memo-app
│
├── index.html
├── style.css
└── script.js

これがWebアプリの最小構成になる。

それぞれの役割

`index.html` → 画面を作る。

`style.css` → デザインを書く。

`script.js` → メモ帳の機能を書く。

このステップで覚えること

HTML

  • Webページの骨組みを作る言語。

CSS

  • Webページの見た目を整える言語。

JavaScript

  • ボタンを押したときなどの動きを作る言語。

Step2 HTMLを作る

今回はまず**「画面だけ」を完成**させる。

まだJavaScriptは書かないので、

  • 入力欄

  • ボタン

  • メモ一覧

だけをHTMLで配置していこう。

完成イメージ

--------------------------
 メモ帳

 [ メモを入力........ ]

 [ 保存 ]

--------------------------
・買い物
・レポート
・ゲーム
--------------------------

index.html

`index.html`を次のように書き換えてみよう。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">

    <!-- スマホでも見やすく表示するための設定 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>メモ帳</title>

    <!-- CSSファイルを読み込む -->
    <link rel="stylesheet" href="style.css">
</head>

<body>

    <h1>メモ帳</h1>

    <!-- メモ入力欄 -->
    <input
        type="text"
        id="memoInput"
        placeholder="メモを入力">

    <!-- 保存ボタン -->
    <button id="saveButton">
        保存
    </button>

    <!-- メモ一覧を表示する場所 -->
    <ul id="memoList">

    </ul>

    <!-- JavaScriptを読み込む -->
    <script src="script.js"></script>

</body>
</html>

コード解説

① `<input>`

<input type="text" id="memoInput" placeholder="メモを入力">

役割
ユーザーがメモを書く場所。

プロパティの意味

  • `type="text"`
    ↳ 1行だけ入力できるテキストボックス

  • `id="memoInput"`
    ↳ JavaScriptからこの入力欄を取得するときの名前。
     後で`document.getElementById("memoInput")`で取得する。

  • `placeholder="メモを入力"`
    ↳ 何も入力されていない時だけ表示される文字

ボタン

  • `<button id="saveButton">保存</button>`
    ↳ 後で`document.getElementById("saveButton")`からクリックイベントを登録する。

③ `<ul>`

  • `<ul id="memoList"></ul>`
    ↳ これは

   Unordered List(順序なしリスト)

   という意味。

   ここに後から

<li>買い物</li>
<li>牛乳</li>

   みたいな要素をJavaScriptで追加していく。

今の画面

ブラウザではこんな感じになればOK。

メモ帳

┌─────────────┐
│ メモを入力   │
└─────────────┘

[ 保存 ]

・

まだ保存ボタンを押しても何も起こらない。

JavaScriptを書いていないからだ。

このステップで覚えること

① `id`

  • HTML要素につける名前。

  • JavaScriptから要素を取得するときによく使う。

② `placeholder`
入力欄が空のときだけ表示される案内文。

③ `<ul>`

リストを表示するためのタグ。

中には後で<li>(リストの項目)を追加していく。

Step3 CSSを作成する

ここではHTMLで作った画面を少し整える。

今回の目的は、

  • 入力欄を見やすくする

  • ボタンを押しやすくする

  • 一覧を整理する

こと。

style.css

`style.css`に以下を書く。

/* ページ全体の設定 */
body {
    font-family: sans-serif;
    width: 400px;
    margin: 50px auto;
}


/* タイトル */
h1 {
    text-align: center;
}


/* メモ入力欄 */
#memoInput {
    width: 70%;
    padding: 8px;
}


/* 保存ボタン */
#saveButton {
    padding: 8px 15px;
}


/* メモ一覧 */
#memoList {
    margin-top: 20px;
}

コード解説

セレクタ

例:

body {

}

これは、

「HTMLの`body`タグに対してデザインを適用する」

という意味。

font-family
`font-family: sans-serif;`
プロパティの意味
文字の種類を指定する。

`sans-serif`は、

  • ゴシック系

  • 見やすい一般的なフォント

を指定している。

③ `width`

`width:400px;`

プロパティの意味

要素の横幅を指定する。

今回は画面全体ではなく、

┌─────────────┐
│   メモ帳    │
│             │
└─────────────┘

中央に小さいアプリ風にするため。

margin

`margin:50px auto;`

プロパティの意味

要素の外側の余白を設定する。

今回の場合、

`50px`
→ 上下に50px空ける

`auto`
→ 左右中央寄せ

になる。

⑤ `padding`

`padding:8px;`

プロパティの意味

要素の内側の余白。

例えば入力欄の場合、

変更前

[メモ入力]

変更後

[  メモ入力  ]

文字と枠の距離が広がる。

今回覚えること

CSSセレクタ

body{}
#memoInput{}

どのHTML要素にデザインを適用するか指定するもの。

margin

要素の外側の余白。

例:

画面
 ↓

  余白
 ┌──────┐
 │要素  │
 └──────┘

padding

要素内部の余白。

┌────────┐
│ 文字   │
└────────┘

↑
ここがpadding

動作確認

ブラウザで`index.html`を開く。

以下のようになればOK。

        メモ帳


[ メモを入力          ][保存]


・

まだ保存機能は動かない。

Step4 JavaScriptを読み込む確認

ここでは、HTMLとJavaScriptが正しく接続されているか確認する。

まだメモ機能は作らない。

まずは、

  • JavaScriptファイルを読み込む

  • JavaScriptが実行される

というWebアプリ開発の基本部分を確認する。

Step4-1 `script.js`を編集する

`script.js`を開いて、以下を書く。

console.log("JavaScriptが読み込まれました");

コード解説

console.log()

console.log("JavaScriptが読み込まれました");

関数の意味

コンソール画面に文字を表示する関数。

例えば、

console.log("Hello");

なら、

Hello

と表示される。

なぜ使う?

開発では、

「このコードはちゃんと動いているか?」

を確認するためによく使う。

これをデバッグという。

Step4-2 ブラウザで確認

`index.html`をブラウザで開く。

その状態で、

`F12キー`

を押す。

または、

右クリック
↓
検証
↓
Console

を開く。

成功すると、

JavaScriptが読み込まれました

と表示される。

もし表示されない場合

確認する場所:

scriptタグ

`index.html`の最後にあるか確認。

<script src="script.js"></script>

ファイル名

HTML:

<script src="script.js"></script>

実際のファイル:`script.js`になっているか確認。

例えば、`scripts.js`だと読み込まれない。

保存

VS Codeなどで編集した場合、

Ctrl + S

で保存する。

このステップで覚えること

JavaScriptファイルの読み込み

HTMLから、

<script src="script.js"></script>

を書くことでJavaScriptを実行できる。

console.log()

開発中の確認によく使うデバッグ用関数。

デバッグ

プログラムの動作確認や問題箇所を探す作業。

Phase1 完了 ✅

ここまでで、

  • HTML → 画面作成

  • CSS → デザイン

  • JavaScript → 読み込み確認

というWebアプリの基本構造が完成した。

次から実際の機能実装に入る。

Phase2 メモ一覧を表示する

ここからは、入力したメモを画面に表示する機能を実装していきます。

現時点では、保存ボタンを押しても画面には何も表示されません。

入力欄
 ↓
保存ボタン
 ↓
(何も表示されない)

このフェーズでは、JavaScriptでHTMLを動的に書き換え、メモ一覧を表示できるようにします。


Step1 メモ一覧を表示する場所を作る

完成イメージ

まずは、メモを表示するための領域をHTMLに追加します。

入力欄

[              ]

[保存]

----------------

・ここにメモが表示される

Step1-1 `index.html`を変更する

保存ボタンの下に、メモ一覧を表示するための`<ul>`を追加します。

変更前

<input id="memoInput" type="text">

<button id="saveButton">
    保存
</button>

変更後

<input id="memoInput" type="text">

<button id="saveButton">
    保存
</button>

<!-- ↓↓↓ Phase2 Step1で追加 ↓↓↓ -->

<!-- メモ一覧を表示する場所 -->
<ul id="memoList"></ul>

ポイント

今回は`id="memoList"`を付けています。

この`id`を使って、JavaScriptからこの要素を取得し、メモ一覧を表示していきます。


Step1-2 `script.js`を変更する

続いて、追加した`<ul>`をJavaScriptから取得します。

変更前

// 保存ボタンを取得
const saveButton = document.getElementById("saveButton");

変更後

// 保存ボタンを取得
const saveButton = document.getElementById("saveButton");

// ↓↓↓ Phase2 Step1で追加 ↓↓↓

// メモ一覧を表示する要素を取得
const memoList = document.getElementById("memoList");

動作確認

現時点では見た目に変化はありません。

ただし、後のステップでこの`memoList`へメモを表示できるようになります。


Step2 メモを画面に表示する

ここでは、JavaScriptでHTMLを書き換え、メモを表示する処理を作成します。

完成イメージ

入力欄

牛乳を買う

↓

保存

↓

・牛乳を買う

Step2-1 表示用の関数を作成する

`script.js`の一番下に、メモ一覧を表示する関数を追加します。

// ↓↓↓ Phase2 Step2で追加 ↓↓↓

// メモ一覧を画面に表示する関数
function renderMemos() {

    // 一覧を一度空にする
    memoList.innerHTML = "";

}

ポイント

今回はまだ表示処理は書きません。

まずは「一覧を表示する関数」を用意するだけです。

今後、メモの追加・編集・削除を行うたびに、この関数を呼び出して画面を更新していきます。


Step2-2 保存ボタンから表示処理を呼び出す

保存ボタンのクリックイベントを変更します。

変更前

// 保存ボタンがクリックされた時の処理
saveButton.addEventListener("click", function() {

    console.log("保存");

});

変更後

// 保存ボタンがクリックされた時の処理
saveButton.addEventListener("click", function() {

    console.log("保存");

    // ↓↓↓ Phase2 Step2で追加 ↓↓↓

    // メモ一覧を更新
    renderMemos();

});

動作確認

保存ボタンを押いても、まだ画面には何も表示されません。

これは正常です。

現在の`renderMemos()`は、

memoList.innerHTML = "";

しか実行していないためです。

次のフェーズでは、この関数の中で実際にメモを表示する処理を実装していきます。


このフェーズで実装したこと

  • メモ一覧を表示する`<ul>`を追加した

  • JavaScriptから一覧表示用の要素を取得した

  • 画面を更新する`renderMemos()`関数を作成した

  • 保存ボタンから一覧更新処理を呼び出せるようにした


次のフェーズ

次のフェーズでは、入力したメモを配列で管理し、実際に画面へ表示する機能を実装していきます。

Phase3 メモ追加機能

ここからは、いよいよ入力したメモを実際に管理していきます。

前のフェーズでは、メモ一覧を表示するための準備として、`renderMemos()`関数とメモ一覧を表示する領域を作成しました。

しかし、現在はまだメモを保存する場所が存在しません。

そのため今回は、JavaScriptの配列を使ってメモを管理できるようにしていきます。


Step1 メモデータを配列で管理する

完成イメージ

メモを入力して保存すると、まずは配列へ追加されるようになります。

入力欄

[牛乳を買う]

↓

保存

↓

memos

[
    "牛乳を買う"
]

この配列をもとに、次のステップで画面へ一覧表示していきます。


Step1-1 `script.js`を変更する

まずは、メモを保存するための配列を作成します。

変更前

// 入力欄を取得
const memoInput = document.getElementById("memoInput");

// 保存ボタンを取得
const saveButton = document.getElementById("saveButton");

// メモ一覧を表示する要素を取得
const memoList = document.getElementById("memoList");

変更後

// 入力欄を取得
const memoInput = document.getElementById("memoInput");

// 保存ボタンを取得
const saveButton = document.getElementById("saveButton");

// メモ一覧を表示する要素を取得
const memoList = document.getElementById("memoList");


// ↓↓↓ Phase3 Step1で追加 ↓↓↓

// メモデータを保存する配列
let memos = [];

コード解説

配列 `[]`

let memos = [];

配列とは

複数のデータをまとめて管理できるデータ型です。

例えば、

let memos = [
    "牛乳を買う",
    "レポートを書く",
    "部屋を掃除する"
];

のように、複数のメモを1つの変数で管理できます。

今回は、この`memos`配列にメモを追加・編集・削除しながらアプリを作っていきます。


このステップで覚えること

  • 配列は複数のデータをまとめて管理するためのデータ型

  • `[]`を使って空の配列を作成できる


Step1-2 保存ボタンを押したら配列へ追加する

続いて、保存ボタンが押されたときに入力したメモを配列へ追加します。

変更前

// 保存ボタンがクリックされた時の処理
saveButton.addEventListener("click", function() {

    console.log("保存");

    // メモ一覧を更新
    renderMemos();

});

変更後

// 保存ボタンがクリックされた時の処理
saveButton.addEventListener("click", function() {

    // 入力欄に入力された文字を取得
    const memoText = memoInput.value;

    // ↓↓↓ Phase3 Step1で変更 ↓↓↓

    // 配列の最後にメモを追加
    memos.push(memoText);

    console.log(memos);

    // メモ一覧を更新
    renderMemos();

});

コード解説

`.value`

memoInput.value

プロパティの意味

入力欄に入力されている文字を取得します。

例えば、

入力欄

[牛乳を買う]

の状態で

memoInput.value

を実行すると、

牛乳を買う

という文字列が取得できます。


`push()`

memos.push(memoText);

メソッドの意味

配列の最後にデータを追加するメソッドです。

例えば、

let memos = [
    "牛乳を買う"
];

に対して

memos.push("レポートを書く");

を実行すると、

[
    "牛乳を買う",
    "レポートを書く"
]

となります。


このステップで覚えること

  • `.value`で入力欄の値を取得できる

  • `push()`で配列の最後にデータを追加できる


Step1-3 動作確認

ブラウザを開き、適当な文字を入力して保存ボタンを押します。

今回はまだ画面には表示されません。

代わりに、ブラウザの開発者ツール(Console)を確認してください。

例えば、

牛乳を買う

と入力して保存すると、

["牛乳を買う"]

のように表示されれば成功です。

さらに、

レポートを書く

を追加すると、

[
    "牛乳を買う",
    "レポートを書く"
]

のように、配列へ順番に追加されていれば正しく実装できています。


Phase3 Step1のまとめ

このステップでは、JavaScriptの配列を使ってメモを管理する仕組みを作成しました。

まだ画面には表示されませんが、メモはすでに配列へ保存されています。

次のステップでは、この配列の中身をもとに、画面へメモ一覧を表示していきます。

Step2 メモ一覧を画面へ表示する

前のステップでは、入力したメモを配列へ保存できるようになりました。

しかし、現在はメモが配列へ保存されているだけで、画面には表示されません。

そこで今回は、配列の中身を読み取り、メモ一覧として画面へ表示する処理を実装していきます。


完成イメージ

入力欄

[牛乳を買う]

↓

保存

↓

memos

[
    "牛乳を買う"
]

↓

画面

・牛乳を買う

Step2-1 `renderMemos()`を変更する

今回は、`renderMemos()`の中にメモを表示する処理を追加します。

変更前

// メモ一覧を画面に表示する関数
function renderMemos() {

    // 一覧を一度空にする
    memoList.innerHTML = "";

}

変更後

// メモ一覧を画面に表示する関数
function renderMemos() {

    // 一覧を一度空にする
    memoList.innerHTML = "";

    // ↓↓↓ Phase3 Step2で追加 ↓↓↓

    // 配列の中身を1つずつ取り出して表示
    memos.forEach(function(memo) {

        memoList.innerHTML += `
            <li>${memo}</li>
        `;

    });

}

コード解説

`forEach()`

memos.forEach(function(memo) {

});

メソッドの意味

配列の中身を先頭から1つずつ取り出して処理を実行するメソッドです。

例えば、

let memos = [
    "牛乳を買う",
    "レポートを書く",
    "部屋を掃除する"
];

に対して、

memos.forEach(function(memo) {

    console.log(memo);

});

を実行すると、

牛乳を買う
レポートを書く
部屋を掃除する

の順番で処理が実行されます。


`innerHTML +=`

memoList.innerHTML += `
    <li>${memo}</li>
`;

意味

HTMLを追加して表示します。

例えば、

最初は

<ul>

</ul>

ですが、

1回目のループで

<ul>
    <li>牛乳を買う</li>
</ul>

さらに2回目では

<ul>
    <li>牛乳を買う</li>
    <li>レポートを書く</li>
</ul>

のように、HTMLが追加されていきます。


テンプレートリテラル

`
    <li>${memo}</li>
`

意味

文字列の中に変数を埋め込める書き方です。

${memo}

の部分へ、

現在取り出しているメモの内容が入ります。

例えば、

memo = "牛乳を買う";

なら、

<li>牛乳を買う</li>

というHTMLになります。


このステップで覚えること

  • `forEach()`は配列の中身を1つずつ処理するメソッド

  • `innerHTML +=`でHTMLを追加できる

  • `${}`を使うと文字列へ変数を埋め込める


Step2-2 動作確認

ブラウザを開き、

牛乳を買う

と入力して保存します。

すると、

・牛乳を買う

と表示されれば成功です。

さらに、

レポートを書く

を保存すると、

・牛乳を買う

・レポートを書く

のように一覧表示されます。


Phase3 Step2のまとめ

このステップでは、配列に保存したデータを画面へ表示する仕組みを実装しました。

`renderMemos()`は、この後の編集機能や削除機能でも何度も利用する重要な関数です。

メモの内容が変更されたら、この関数を呼び出して画面を更新するという流れでアプリを作っていきます。

Phase4 localStorage機能

ここまでで、メモの追加・一覧表示ができるようになりました。

しかし、現在のアプリにはまだ一つ問題があります。

それは、ページを再読み込みするとメモがすべて消えてしまうことです。

メモを追加

↓

・牛乳を買う
・レポートを書く

↓

F5で再読み込み

↓

メモがすべて消える...

これは、現在メモを保存している`memos`配列がメモリ上にしか存在しないためです。

そこでこのフェーズでは、ブラウザに用意されているlocalStorageを利用して、メモを保存できるようにしていきます。


Step1 メモデータをブラウザに保存する

完成イメージ

保存ボタンを押すと、

保存

↓

memos配列

↓

localStorageへ保存

という流れになります。

次のステップでは、この保存したデータを読み込めるようにしていきます。


Step1-1 `script.js`を変更する

保存ボタンがクリックされたときに、`memos`配列を`localStorage`へ保存する処理を追加します。

変更前

// 保存ボタンがクリックされた時の処理
saveButton.addEventListener("click", function() {

    // 入力欄に入力された文字を取得
    const memoText = memoInput.value;

    // 配列の最後に追加
    memos.push(memoText);

    // メモ一覧を更新
    renderMemos();

    // 入力欄を空にする
    memoInput.value = "";

});

変更後

// 保存ボタンがクリックされた時の処理
saveButton.addEventListener("click", function() {

    // 入力欄に入力された文字を取得
    const memoText = memoInput.value;

    // 配列の最後に追加(Phase3 Step1で解説)
    memos.push(memoText);

    // ↓↓↓ Phase4 Step1で追加 ↓↓↓

    // 配列をlocalStorageへ保存
    localStorage.setItem(
        "memos",
        JSON.stringify(memos)
    );

    // メモ一覧を更新(Phase3 Step2で解説)
    renderMemos();

    // 入力欄を空にする(Phase3で解説)
    memoInput.value = "";

});

コード解説

`localStorage.setItem()`

localStorage.setItem("memos", JSON.stringify(memos));

メソッドの意味

ブラウザの`localStorage`へデータを保存するメソッドです。

書式は次のようになります。

localStorage.setItem("キー", 保存するデータ);

今回の場合、

localStorage.setItem("memos", ...);

としているので、

`memos`という名前でデータを保存するという意味になります。


`JSON.stringify()`

JSON.stringify(memos)

メソッドの意味

配列やオブジェクトを、文字列へ変換するメソッドです。

`localStorage`へ保存できるのは文字列だけなので、そのまま配列を保存することはできません。

例えば、

let memos = [
    "牛乳を買う",
    "レポートを書く"
];

JSON.stringify(memos);

とすると、

["牛乳を買う","レポートを書く"]

という文字列へ変換されます。

この文字列を`localStorage`へ保存することで、配列の内容を保持できるようになります。


このステップで覚えること

  • `localStorage.setItem()`でブラウザへデータを保存できる

  • `JSON.stringify()`で配列を文字列へ変換できる


Step1-2 動作確認

ブラウザでメモを追加します。

牛乳を買う

保存

画面には今まで通り

・牛乳を買う

と表示されます。

現時点では見た目の変化はありません。

しかし、メモはすでに`localStorage`へ保存されています。

Chromeの開発者ツールから確認してみましょう。

  1. F12を押して開発者ツールを開く

  2. Applicationタブを開く

  3. 左メニューのLocal Storageを選択

  4. 現在開いているサイトをクリック

すると、

| Key | Value |
|------|-------|
| memos | `["牛乳を買う"]` |

のようなデータが保存されていれば成功です。


Phase4 Step1のまとめ

このステップでは、メモデータを`localStorage`へ保存する機能を実装しました。

これにより、メモはブラウザ内へ保存されるようになりました。

ただし、まだ保存したデータを読み込む処理は実装していません。

そのため、ページを再読み込みすると画面には何も表示されません。

次のステップでは、`localStorage`へ保存したメモを読み込み、ページを開いたときに自動で一覧表示されるようにしていきます。

Step2 保存したメモを読み込む

前のステップでは、`memos`配列を`localStorage`へ保存できるようになりました。

しかし、まだ保存したデータを読み込む処理は実装していません。

そのため、ページを再読み込みすると、`localStorage`にはデータが残っているにもかかわらず、画面には何も表示されません。

メモを追加

↓

localStorageへ保存

↓

F5で再読み込み

↓

memos配列は空

↓

画面も空

そこで今回は、ページを開いたときに`localStorage`からデータを読み込み、メモ一覧を復元できるようにしていきます。


完成イメージ

ページを開く

↓

localStorageからデータを取得

↓

memos配列へ代入

↓

renderMemos()

↓

以前保存したメモが表示される

Step2-1 `script.js`を変更する

まずは、`localStorage`からデータを取得する処理を追加します。

変更前

// メモデータを保存する配列
let memos = [];

変更後

// メモデータを保存する配列
let memos = [];


// ↓↓↓ Phase4 Step2で追加 ↓↓↓

// localStorageから保存済みデータを取得
const savedMemos = localStorage.getItem("memos");

// データが存在する場合
if (savedMemos !== null) {

    // 配列へ戻す
    memos = JSON.parse(savedMemos);

}

コード解説

`localStorage.getItem()`

localStorage.getItem("memos");

メソッドの意味

`localStorage`に保存されているデータを取得するメソッドです。

書式は次のようになります。

localStorage.getItem("キー");

今回の場合は、

localStorage.getItem("memos");

としているので、

`memos`という名前で保存されているデータを取得しています。

データが存在しない場合は、

null

が返されます。


`JSON.parse()`

JSON.parse(savedMemos)

メソッドの意味

文字列をJavaScriptのデータへ戻すメソッドです。

前のステップでは、

JSON.stringify()

を使って配列を文字列へ変換しました。

今回はその逆で、

["牛乳を買う","レポートを書く"]

という文字列を、

[
    "牛乳を買う",
    "レポートを書く"
]

という配列へ戻しています。


このステップで覚えること

  • `localStorage.getItem()`で保存済みデータを取得できる

  • `JSON.parse()`で文字列を配列へ戻せる


Step2-2 ページを開いたときに一覧を表示する

データを読み込めるようになったので、ページが開いたタイミングで一覧を表示する処理を追加します。

変更前

// メモ一覧を画面に表示する関数
function renderMemos() {

    // 一覧を一度空にする
    memoList.innerHTML = "";

    memos.forEach(function(memo) {

        memoList.innerHTML += `
            <li>${memo}</li>
        `;

    });

}

変更後

// メモ一覧を画面に表示する関数
function renderMemos() {

    // 一覧を一度空にする
    memoList.innerHTML = "";

    memos.forEach(function(memo) {

        memoList.innerHTML += `
            <li>${memo}</li>
        `;

    });

}


// ↓↓↓ Phase4 Step2で追加 ↓↓↓

// ページ読み込み時に保存済みメモを表示
renderMemos();

コード解説

今回は新しく登場する文法やメソッドはありません。

renderMemos();

をページ読み込み時に実行することで、

`memos`配列へ読み込まれたデータが画面へ表示されます。


このステップで覚えること

  • ページ読み込み時にも`renderMemos()`を実行すると、保存済みデータを表示できる


Step2-3 動作確認

  1. メモをいくつか追加します。

・牛乳を買う
・レポートを書く
  1. F5キーを押してページを再読み込みします。

すると、

・牛乳を買う
・レポートを書く

がそのまま表示されていれば成功です。

ブラウザを閉じて再度開いても、メモが保持されていることも確認してみましょう。


Phase4のまとめ

このフェーズでは、`localStorage`を利用してメモデータを保存・読み込みできるようにしました。

実装した内容は以下の2つです。

  • `localStorage`へメモデータを保存する機能

  • ページ読み込み時に保存済みデータを復元する機能

これにより、ブラウザを再読み込みしてもメモが消えない、実用的なメモ帳アプリが完成しました。

Phase5 編集機能

ここまでで、メモの追加・一覧表示・ブラウザへの保存ができるようになりました。

しかし、一度保存したメモは修正することができません

例えば、

・牛乳を買う

と保存した後に、

・牛乳を買う(スーパー)

へ変更したい場合でも、一度削除して作り直す必要があります。

そこでこのフェーズでは、メモを後から編集できる機能を実装していきます。


Step1 編集ボタンを追加する

まずは、各メモの横に編集ボタンを表示します。

今回はボタンを表示するだけで、クリックしてもまだ何も起こりません。


完成イメージ

・牛乳を買う      [編集]

・レポートを書く  [編集]

Step1-1 `renderMemos()`を変更する

現在は、メモだけを表示しています。

ここへ編集ボタンを追加していきます。

変更前

// メモ一覧を画面に表示する関数
function renderMemos() {

    // 一覧を一度空にする
    memoList.innerHTML = "";

    memos.forEach(function(memo) {

        memoList.innerHTML += `
            <li>${memo}</li>
        `;

    });

}

変更後

// メモ一覧を画面に表示する関数
function renderMemos() {

    // 一覧を一度空にする
    memoList.innerHTML = "";

    memos.forEach(function(memo, index) {

        memoList.innerHTML += `
            <li>
                ${memo}

                <!-- ↓↓↓ Phase5 Step1で追加 ↓↓↓ -->

                <button
                    data-index="${index}"
                >
                    編集
                </button>

            </li>
        `;

    });

}

コード解説

`index`

memos.forEach(function(memo, index) {

});

意味

`forEach()`では、現在取り出しているデータだけでなく、その何番目のデータなのかも取得できます。

例えば、

let memos = [
    "牛乳を買う",
    "レポートを書く",
    "部屋を掃除する"
];

の場合、

| memo | index |
|------|------:|
| 牛乳を買う | 0 |
| レポートを書く | 1 |
| 部屋を掃除する | 2 |

という値になります。

この番号を利用することで、

「どのメモを編集するのか」

を後で判別できるようになります。


`data-index`

<button data-index="${index}">

属性の意味

HTMLには、`data-`から始まるカスタムデータ属性を付けることができます。

今回は、

data-index="0"

data-index="1"

のように、

メモの番号をボタンへ持たせています。

後のステップでは、この番号を取得して、

どのメモを編集するのか

を判断していきます。


このステップで覚えること

  • `forEach()`では現在の要素番号(`index`)も取得できる

  • `data-`属性を使うとHTML要素へ任意のデータを持たせられる


Step1-2 動作確認

ブラウザを開き、メモを追加します。

例えば、

・牛乳を買う

・レポートを書く

と表示されている場合、

各メモの右側に

[編集]

ボタンが表示されていれば成功です。

現時点では、クリックしても何も起こりません。

これは正常な動作です。


Phase5 Step1のまとめ

このステップでは、各メモの横に編集ボタンを追加しました。

また、編集ボタンへ`data-index`を設定することで、「どのメモに対応するボタンなのか」を判別できるようになりました。

この番号は、次のステップで編集ボタンが押されたときの処理を実装する際に利用します。

Step2 編集ボタンにクリック処理を追加する

前のステップでは、各メモの横に編集ボタンを追加しました。

しかし、現時点ではボタンをクリックしても何も起こりません。

そこで今回は、編集ボタンがクリックされたことを検知できるようにしていきます。

なお、このステップではクリックイベントを追加するだけで、実際に編集する処理はまだ実装しません。


完成イメージ

・牛乳を買う      [編集]

↓

編集ボタンをクリック

↓

クリックイベントが実行される

Step2-1 編集ボタンを取得する

まずは、画面に表示されている編集ボタンをJavaScriptから取得します。

`renderMemos()`の最後へ追加してください。

変更前

// メモ一覧を画面に表示する関数
function renderMemos() {

    // 一覧を一度空にする
    memoList.innerHTML = "";

    memos.forEach(function(memo, index) {

        memoList.innerHTML += `
            <li>
                ${memo}

                <button
                    data-index="${index}"
                >
                    編集
                </button>

            </li>
        `;

    });

}

変更後

// メモ一覧を画面に表示する関数
function renderMemos() {

    // 一覧を一度空にする
    memoList.innerHTML = "";

    memos.forEach(function(memo, index) {

        memoList.innerHTML += `
            <li>
                ${memo}

                <button
                    class="editButton"
                    data-index="${index}"
                >
                    編集
                </button>

            </li>
        `;

    });

    // ↓↓↓ Phase5 Step2で追加 ↓↓↓

    // 編集ボタンをすべて取得
    const editButtons = document.querySelectorAll(".editButton");

}

コード解説

`class`

class="editButton"

属性の意味

同じ役割を持つ要素へ名前を付ける属性です。

今回は、

<button class="editButton">

とすることで、すべての編集ボタンをまとめて取得できるようになります。


`querySelectorAll()`

document.querySelectorAll(".editButton");

メソッドの意味

指定した条件に一致する要素をすべて取得するメソッドです。

今回は、

".editButton"

を指定しているので、

<button class="editButton">

が付いているボタンをすべて取得しています。


このステップで覚えること

  • `class`は同じ役割の要素へ名前を付ける属性

  • `querySelectorAll()`は条件に一致する要素をすべて取得する


Step2-2 編集ボタンへクリックイベントを追加する

取得した編集ボタンへクリックイベントを設定します。

変更前

// 編集ボタンをすべて取得
const editButtons = document.querySelectorAll(".editButton");

変更後

// 編集ボタンをすべて取得
const editButtons = document.querySelectorAll(".editButton");

// ↓↓↓ Phase5 Step2で追加 ↓↓↓

// 編集ボタンそれぞれへクリックイベントを設定
editButtons.forEach(function(button) {

    button.addEventListener("click", function() {

        console.log("編集ボタンが押されました");

    });

});

コード解説

今回は新しく登場する文法やメソッドはありません。

  • `forEach()`はPhase3 Step2で解説しました。

  • `addEventListener()`はこれまで保存ボタンのクリックイベントで使用してきました。

今回は、それらを組み合わせてすべての編集ボタンへクリックイベントを設定しています。


このステップで覚えること

  • `querySelectorAll()`で取得した複数の要素にも、`forEach()`でイベントを設定できる


Step2-3 動作確認

ブラウザを開き、編集ボタンをクリックします。

Chromeの開発者ツール(Console)を開くと、

編集ボタンが押されました

と表示されれば成功です。

どの編集ボタンを押しても、同じメッセージが表示されることを確認してください。


Phase5 Step2のまとめ

このステップでは、編集ボタンへクリックイベントを追加しました。

これにより、「編集ボタンが押された」という操作をJavaScriptで検知できるようになりました。

次のステップでは、クリックされたメモの内容を入力欄へ表示し、編集できるようにしていきます。

Step3 編集内容を入力欄へ反映する

前のステップでは、編集ボタンがクリックされたことを検知できるようになりました。

しかし、まだ「どのメモを編集するのか」は判断できません。

そこで今回は、クリックされた編集ボタンに対応するメモを取得し、その内容を入力欄へ表示できるようにしていきます。


完成イメージ

・牛乳を買う      [編集]

↓

編集をクリック

↓

入力欄

[牛乳を買う]

これで、入力欄の内容を書き換えて更新できる準備が整います。


Step3-1 編集するメモの番号を取得する

まずは、クリックされた編集ボタンに設定されている`data-index`を取得します。

変更前

// 編集ボタンそれぞれへクリックイベントを設定
editButtons.forEach(function(button) {

    button.addEventListener("click", function() {

        console.log("編集ボタンが押されました");

    });

});

変更後

// 編集ボタンそれぞれへクリックイベントを設定
editButtons.forEach(function(button) {

    button.addEventListener("click", function() {

        // ↓↓↓ Phase5 Step3で追加 ↓↓↓

        // 編集するメモの番号を取得
        const index = button.dataset.index;

        console.log(index);

    });

});

コード解説

`dataset`

button.dataset.index

プロパティの意味

HTMLに設定した`data-`属性の値を取得できます。

例えば、

<button data-index="2">
    編集
</button>

の場合、

button.dataset.index

を実行すると、

2

が取得できます。

今回は、この番号を使ってどのメモを編集するのかを判別します。


このステップで覚えること

  • `dataset`を使うと、`data-`属性の値を取得できる


Step3-2 入力欄へメモを表示する

取得した番号を利用して、対応するメモを入力欄へ表示します。

変更前

// 編集するメモの番号を取得
const index = button.dataset.index;

console.log(index);

変更後

// 編集するメモの番号を取得
const index = button.dataset.index;

// ↓↓↓ Phase5 Step3で追加 ↓↓↓

// 編集するメモを入力欄へ表示
memoInput.value = memos[index];

コード解説

今回は新しく登場する文法やメソッドはありません。

memos[index]

は、配列から指定した番号のデータを取得しています。

また、

memoInput.value = ...

Phase3で解説したように、入力欄の値を変更する処理です。

この2つを組み合わせることで、

配列

↓

指定したメモを取得

↓

入力欄へ表示

という流れを実現しています。


このステップで覚えること

  • `配列[index]`で指定した番号のデータを取得できる


Step3-3 動作確認

ブラウザを開き、以下のようにメモを追加します。

・牛乳を買う

・レポートを書く

次に、

「レポートを書く」の編集ボタンをクリックします。

すると、入力欄に

レポートを書く

と表示されれば成功です。

別のメモでも同じように、クリックしたメモの内容が入力欄へ表示されることを確認してみましょう。


Phase5 Step3のまとめ

このステップでは、クリックした編集ボタンから`data-index`を取得し、その番号を使って対応するメモを入力欄へ表示できるようにしました。

これで、編集したいメモの内容を入力欄へ読み込めるようになりました。
次のステップでは、編集した内容を保存し、既存のメモを更新する機能を実装していきます。

Step4 編集内容を保存して更新する

前のステップでは、編集ボタンをクリックすると、メモの内容が入力欄へ表示されるようになりました。

しかし、入力内容を書き換えて保存ボタンを押しても、新しいメモが追加されるだけで、元のメモは変更されません。

編集前

・牛乳を買う

↓

編集

↓

牛乳を買う(スーパー)

↓

保存

↓

・牛乳を買う
・牛乳を買う(スーパー)

そこで今回は、新しいメモを追加するのではなく、既存のメモを更新する処理を実装していきます。


完成イメージ

・牛乳を買う

↓

編集

↓

牛乳を買う(スーパー)

↓

更新

↓

・牛乳を買う(スーパー)

Step4-1 編集中のメモ番号を管理する

まずは、現在どのメモを編集しているのかを保存する変数を作成します。

変更前

// メモデータを保存する配列
let memos = [];

変更後

// メモデータを保存する配列
let memos = [];

// ↓↓↓ Phase5 Step4で追加 ↓↓↓

// 編集中のメモ番号
let editingIndex = null;

コード解説

`null`

let editingIndex = null;

値の意味

`null`は、

「何も入っていない」

ことを表す特別な値です。

今回は、

editingIndex = null;

なら、

現在は編集していない

という意味になります。

後ほど編集ボタンが押されたら、

editingIndex = 1;

のように、編集するメモの番号を保存します。


このステップで覚えること

  • `null`は「値が存在しない」ことを表す


Step4-2 編集ボタンを押したときに編集番号を保存する

編集ボタンのクリックイベントを変更します。

変更前

// 編集するメモの番号を取得
const index = button.dataset.index;

// 編集するメモを入力欄へ表示
memoInput.value = memos[index];

変更後

// 編集するメモの番号を取得
const index = button.dataset.index;

// ↓↓↓ Phase5 Step4で追加 ↓↓↓

// 編集中のメモ番号を保存
editingIndex = index;

// 編集するメモを入力欄へ表示
memoInput.value = memos[index];

// 保存ボタンの文字を変更
saveButton.textContent = "更新";

コード解説

`textContent`

saveButton.textContent = "更新";

プロパティの意味

要素の文字を変更するプロパティです。

今回は、

<button>保存</button>

を、

<button>更新</button>

へ変更しています。

これにより、ユーザーにも現在編集モードであることが分かりやすくなります。


このステップで覚えること

  • `textContent`で要素の文字を変更できる


Step4-3 保存処理を編集対応にする

最後に、保存ボタンが押されたときの処理を変更します。

変更前

// 配列の最後に追加
memos.push(memoText);

変更後

// ↓↓↓ Phase5 Step4で変更 ↓↓↓

// 編集中かどうか判定
if (editingIndex === null) {

    // 新規追加
    memos.push(memoText);

} else {

    // 編集内容で上書き
    memos[editingIndex] = memoText;

    // 編集モードを終了
    editingIndex = null;

    // 保存ボタンの文字を元へ戻す
    saveButton.textContent = "保存";

}

コード解説

今回は新しく登場する文法やメソッドはありません。

memos[editingIndex] = memoText;

は、配列の指定した位置のデータを新しい値へ変更しています。

また、

editingIndex = null;

としているのは、更新が終わったら編集モードを終了するためです。


このステップで覚えること

  • `配列[index] = 値`で配列のデータを更新できる


Step4-4 動作確認

ブラウザを開き、以下の手順を試してみましょう。

  1. メモを追加します。

・牛乳を買う
  1. 「編集」をクリックします。

すると、

  • 入力欄へメモが表示される

  • 保存ボタンが「更新」に変わる

ことを確認してください。

  1. 内容を変更します。

牛乳を買う(スーパー)
  1. 「更新」をクリックします。

すると、

・牛乳を買う(スーパー)

と表示され、

保存ボタンも再び「保存」に戻れば成功です。


Phase5のまとめ

このフェーズでは、既存のメモを編集できる機能を実装しました。

実装した内容は以下の4つです。

  • 編集ボタンを追加した

  • 編集ボタンのクリックイベントを設定した

  • 編集するメモを入力欄へ表示できるようにした

  • 編集した内容で既存のメモを更新できるようにした

これで、メモの追加だけでなく、後から内容を修正できるメモ帳アプリになりました。

Phase6 削除機能

ここまでで、メモの追加・編集・保存ができるようになりました。

しかし、不要になったメモを削除することはまだできません。

例えば、

・牛乳を買う
・レポートを書く

のうち、

・レポートを書く

が不要になった場合でも、そのまま残り続けてしまいます。

そこでこのフェーズでは、不要なメモを削除できる機能を実装していきます。


Step1 削除ボタンを追加する

まずは、各メモの横に削除ボタンを表示します。

今回はボタンを表示するだけで、クリックしてもまだ何も起こりません。


完成イメージ

・牛乳を買う      [編集] [削除]

・レポートを書く  [編集] [削除]

Step1-1 `renderMemos()`を変更する

編集ボタンの横へ削除ボタンを追加します。

変更前

memoList.innerHTML += `
    <li>
        ${memo}

        <button
            class="editButton"
            data-index="${index}"
        >
            編集
        </button>

    </li>
`;

変更後

memoList.innerHTML += `
    <li>
        ${memo}

        <button
            class="editButton"
            data-index="${index}"
        >
            編集
        </button>

        <!-- ↓↓↓ Phase6 Step1で追加 ↓↓↓ -->

        <button
            class="deleteButton"
            data-index="${index}"
        >
            削除
        </button>

    </li>
`;

コード解説

今回は新しく登場する文法やメソッドはありません。

追加した削除ボタンには、

class="deleteButton"

を設定しています。

これにより、後のステップで

document.querySelectorAll(".deleteButton")

を使って、すべての削除ボタンを取得できるようになります。

また、

data-index="${index}"

も編集ボタンと同じ役割です。

削除ボタンがどのメモに対応しているのかを判別するために利用します。


このステップで覚えること

今回は新しく覚える文法やメソッドはありません。


Step1-2 動作確認

ブラウザを開き、メモをいくつか追加します。

例えば、

・牛乳を買う

・レポートを書く

と表示されている場合、

各メモの右側に

[編集] [削除]

と表示されていれば成功です。

現時点では、削除ボタンをクリックしても何も起こりません。

これは正常な動作です。


Phase6 Step1のまとめ

このステップでは、各メモの横に削除ボタンを追加しました。

また、削除ボタンにも`class`と`data-index`を設定することで、後のステップでクリックイベントを設定し、どのメモを削除するか判別できる準備が整いました。

Step2 削除ボタンにクリック処理を追加する

前のステップでは、各メモの横に削除ボタンを追加しました。

しかし、現時点では削除ボタンをクリックしても何も起こりません。

そこで今回は、削除ボタンがクリックされたときにメモを削除する処理を実装していきます。


完成イメージ

・牛乳を買う      [編集] [削除]

↓

削除をクリック

↓

配列から削除

↓

localStorageを更新

↓

画面を更新

・牛乳を買う

Step2-1 削除ボタンを取得する

まずは、削除ボタンをJavaScriptから取得します。

`renderMemos()`の最後へ追加してください。

変更前

// 編集ボタンをすべて取得
const editButtons = document.querySelectorAll(".editButton");

// 編集ボタンそれぞれへクリックイベントを設定
editButtons.forEach(function(button) {

    // ...
});

変更後

// 編集ボタンをすべて取得
const editButtons = document.querySelectorAll(".editButton");

// 編集ボタンそれぞれへクリックイベントを設定
editButtons.forEach(function(button) {

    // ...
});


// ↓↓↓ Phase6 Step2で追加 ↓↓↓

// 削除ボタンをすべて取得
const deleteButtons = document.querySelectorAll(".deleteButton");

コード解説

今回は新しく登場する文法やメソッドはありません。

削除ボタンにも編集ボタンと同様に、

document.querySelectorAll(".deleteButton")

を使用することで、すべての削除ボタンを取得できます。


このステップで覚えること

今回は新しく覚える文法やメソッドはありません。


Step2-2 削除ボタンへクリックイベントを追加する

取得した削除ボタンへクリックイベントを設定します。

変更前

// 削除ボタンをすべて取得
const deleteButtons = document.querySelectorAll(".deleteButton");

変更後

// 削除ボタンをすべて取得
const deleteButtons = document.querySelectorAll(".deleteButton");

// ↓↓↓ Phase6 Step2で追加 ↓↓↓

// 削除ボタンそれぞれへクリックイベントを設定
deleteButtons.forEach(function(button) {

    button.addEventListener("click", function() {

        // 削除するメモの番号を取得
        const index = button.dataset.index;

        // 配列からメモを削除
        memos.splice(index, 1);

        // localStorageを更新
        localStorage.setItem(
            "memos",
            JSON.stringify(memos)
        );

        // メモ一覧を更新
        renderMemos();

    });

});

コード解説

`splice()`

memos.splice(index, 1);

メソッドの意味

配列の指定した位置からデータを削除するメソッドです。

書式は次のようになります。

配列.splice(開始位置, 削除する個数);

例えば、

let memos = [
    "牛乳を買う",
    "レポートを書く",
    "部屋を掃除する"
];

に対して、

memos.splice(1, 1);

を実行すると、

[
    "牛乳を買う",
    "部屋を掃除する"
]

となり、2番目のデータだけが削除されます。


このステップで覚えること

  • `splice()`は配列の指定した位置のデータを削除できる


Step2-3 動作確認

ブラウザを開き、メモをいくつか追加します。

・牛乳を買う

・レポートを書く

・部屋を掃除する

次に、「レポートを書く」の削除ボタンをクリックします。

すると、

・牛乳を買う

・部屋を掃除する

のように、クリックしたメモだけが削除されれば成功です。

続けてページを再読み込みし、削除したメモが復元されないことも確認してみましょう。


Phase6のまとめ

このフェーズでは、不要なメモを削除できる機能を実装しました。

実装した内容は以下の2つです。

  • 各メモへ削除ボタンを追加した

  • 削除ボタンから配列・`localStorage`・画面を更新できるようにした

これで、メモの追加・編集・削除・保存という基本的なCRUD機能をすべて備えたメモ帳アプリが完成しました。

Phase7 リファクタリング

ここまでで、メモ帳アプリの基本機能はすべて完成しました。

しかし、実際に使ってみると、まだ改善できる点があります。

例えば、入力欄が空の状態でも保存ボタンを押せてしまい、空のメモが作成されてしまいます。

入力欄

[        ]

↓

保存

↓

・

このようなメモは意味がなく、ユーザーにとっても使いにくいアプリになってしまいます。

そこでこのフェーズでは、より使いやすいアプリにするための改善を行っていきます。


Step1 空のメモを保存できないようにする

今回は、入力欄が空の場合は保存処理を行わないようにします。

また、スペースだけが入力されている場合も保存できないようにしていきます。


完成イメージ

入力欄

[        ]

↓

保存

↓

何も起こらない

Step1-1 保存処理を変更する

保存ボタンがクリックされたときの処理へ入力チェックを追加します。

変更前

// 入力欄に入力された文字を取得
const memoText = memoInput.value;

// 編集中かどうか判定
if (editingIndex === null) {

    // 新規追加
    memos.push(memoText);

} else {

    // 編集内容で上書き
    memos[editingIndex] = memoText;

    // 編集モードを終了
    editingIndex = null;

    // 保存ボタンの文字を元へ戻す
    saveButton.textContent = "保存";

}

変更後

// 入力欄に入力された文字を取得
const memoText = memoInput.value.trim();

// ↓↓↓ Phase7 Step1で追加 ↓↓↓

// 入力が空なら処理を終了
if (memoText === "") {
    return;
}

// 編集中かどうか判定
if (editingIndex === null) {

    // 新規追加
    memos.push(memoText);

} else {

    // 編集内容で上書き
    memos[editingIndex] = memoText;

    // 編集モードを終了
    editingIndex = null;

    // 保存ボタンの文字を元へ戻す
    saveButton.textContent = "保存";

}

コード解説

`trim()`

memoInput.value.trim()

メソッドの意味

文字列の先頭と末尾にある空白を削除するメソッドです。

例えば、

"   牛乳を買う   "

に対して、

trim()

を実行すると、

"牛乳を買う"

になります。

また、

"     "

のように空白だけが入力されていた場合は、

""

という空文字になります。

そのため、空白だけの入力も判定できるようになります。


`return`

return;

意味

現在実行している関数の処理を終了します。

今回は、

if (memoText === "") {
    return;
}

としているため、入力欄が空だった場合は、それ以降の保存処理は実行されません。


このステップで覚えること

  • `trim()`で文字列の前後の空白を削除できる

  • `return`で関数の処理を途中で終了できる


Step1-2 動作確認

ブラウザを開き、以下の内容で保存を試してみましょう。

① 何も入力しない

[        ]

保存

メモは追加されない。


② 半角スペースだけ入力する

[      ]

保存

メモは追加されない。


③ 通常のメモを入力する

牛乳を買う

保存

・牛乳を買う

と表示されれば成功です。


Phase7 Step1のまとめ

このステップでは、空のメモや空白だけのメモを保存できないようにしました。

これにより、不要なメモが作成されることを防ぎ、より実用的なメモ帳アプリになりました。

Step2 Enterキーでも保存できるようにする

前のステップでは、空のメモを保存できないようにしました。

しかし、現在メモを保存するには、毎回保存ボタンをクリックする必要があります。

多くのメモアプリでは、入力後にEnterキーを押すだけで保存できます。

そこで今回は、Enterキーを押したときにも保存処理が実行されるようにしていきます。


完成イメージ

入力欄

[牛乳を買う]

↓

Enterキー

↓

保存

↓

・牛乳を買う

Step2-1 Enterキーを押したことを検知する

まずは、入力欄でキーが押されたことを取得します。

変更前

// ページ読み込み時に保存済みメモを表示
renderMemos();

変更後

// ページ読み込み時に保存済みメモを表示
renderMemos();


// ↓↓↓ Phase7 Step2で追加 ↓↓↓

// 入力欄でキーが押された時の処理
memoInput.addEventListener("keydown", function(event) {

    console.log(event.key);

});

コード解説

`"keydown"`

memoInput.addEventListener("keydown", ...);

イベントの意味

キーボードのキーが押されたときに実行されるイベントです。

今回は入力欄に対して設定しているため、入力欄が選択されている状態でキーを押すと処理が実行されます。


`event.key`

event.key

プロパティの意味

押されたキーの名前を取得できます。

例えば、

| 押したキー | `event.key` |
|------------|-------------|
| Enter | `"Enter"` |
| A | `"a"` |
| B | `"b"` |
| Escape | `"Escape"` |


このステップで覚えること

  • `"keydown"`はキーが押されたときに発生するイベント

  • `event.key`で押されたキーを取得できる


Step2-2 Enterキーで保存処理を実行する

Enterキーが押された場合だけ、保存ボタンをクリックしたときと同じ処理を実行します。

変更前

// 入力欄でキーが押された時の処理
memoInput.addEventListener("keydown", function(event) {

    console.log(event.key);

});

変更後

// 入力欄でキーが押された時の処理
memoInput.addEventListener("keydown", function(event) {

    // ↓↓↓ Phase7 Step2で変更 ↓↓↓

    // Enterキーが押された場合
    if (event.key === "Enter") {

        // 保存ボタンをクリック
        saveButton.click();

    }

});

コード解説

`click()`

saveButton.click();

メソッドの意味

JavaScriptからボタンのクリックを実行するメソッドです。

今回は、

saveButton.click();

とすることで、保存ボタンを実際にクリックしたときと同じ処理を実行しています。

そのため、保存ボタン用の処理をもう一度書く必要がありません。


このステップで覚えること

  • `click()`を使うとJavaScriptからクリックイベントを実行できる


Step2-3 動作確認

ブラウザを開き、入力欄へ

牛乳を買う

と入力します。

続いて、保存ボタンを押さずにEnterキーを押してください。

すると、

・牛乳を買う

が追加されれば成功です。

また、編集モードでも同様にEnterキーで更新できることを確認してみましょう。


Phase7のまとめ

このフェーズでは、メモ帳アプリをより使いやすくするための改善を行いました。

実装した内容は以下の2つです。

  • 空のメモや空白だけのメモを保存できないようにした

  • Enterキーでも保存・更新できるようにした

これで、メモの追加・編集・削除・保存・復元に加え、入力時の操作性も向上し、より実用的なメモ帳アプリが完成しました。

完成!


完成したメモ帳アプリ

まとめ

ここまでお疲れさまでした!

今回のハンズオンでは、JavaScriptの基礎を学びながら、実際に動作するメモ帳アプリを一から実装しました。

「ただ文法を覚える」のではなく、「実際にアプリを作る」という体験を通して、JavaScriptを使った開発の流れを掴めたのではないでしょうか。


振り返り

今回の実装では、以下のようなJavaScriptの基礎知識を学びました。

DOM操作

  • `querySelector()`

  • `querySelectorAll()`

  • `innerHTML`

  • `textContent`

  • `value`

HTMLの要素を取得・変更する方法を学び、画面を自由に更新できるようになりました。


イベント処理

  • `addEventListener()`

  • `click`

  • `keydown`

ボタンのクリックやキーボード入力に応じて処理を実行する方法を学びました。


配列操作

  • `push()`

  • `splice()`

  • `forEach()`

配列へデータを追加・削除・一覧表示する方法を学びました。


localStorage

  • `setItem()`

  • `getItem()`

  • `JSON.stringify()`

  • `JSON.parse()`

ブラウザへデータを保存し、ページを再読み込みしてもデータを保持する方法を学びました。


その他の基礎知識

  • `if`文

  • `return`

  • `trim()`

  • `dataset`

  • `class`

  • `data-`属性

アプリ開発でよく利用する基本的な文法やプロパティについても学びました。


アプリ開発の考え方

今回のハンズオンでは、コードを書く技術だけではなく、

  • 機能ごとに段階的に実装する

  • 画面表示を関数へまとめる

  • 配列をデータの管理場所として利用する

  • localStorageを使ってデータを永続化する

といった、実際のアプリ開発でも重要となる考え方も学びました。


今後つけたい拡張機能

ここまで完成できたら、次は機能を増やしてより実践的なアプリへ進化させてみましょう。

★☆☆ 初級

JavaScriptの基礎をさらに身に付けたい人向け

  • メモの件数を表示する

  • 削除前に確認ダイアログを表示する

  • 作成日時・更新日時を表示する

  • メモを検索できるようにする

  • メモを文字数順・作成順で並び替える


★★☆ 中級

少し設計を意識したアプリを作ってみたい人向け

  • カテゴリー機能

  • お気に入り機能

  • タグ機能

  • ピン留め機能

  • ダークモード対応

  • 一括削除機能


★★★ 上級

実際のWebアプリに近い構成へ挑戦したい人向け

  • Markdown対応メモ

  • ドラッグ&ドロップによる並び替え

  • データのエクスポート・インポート

  • IndexedDBを使った大量データ管理

  • FirebaseやSupabaseを利用したクラウド同期

  • ログイン機能付きメモアプリへの発展


おわりに

ここまで記事を読んでいただき、ありがとうございました!

JavaScriptは、「文法を覚えるだけ」ではなかなか身に付きません。

今回のように、小さなアプリを一つずつ作りながら学習していくことで、知識が実践的なスキルへと変わっていきます。

もしこの記事が役に立ったら、ぜひ次は今回紹介した拡張機能にも挑戦してみてください。

一つひとつ機能を追加していくことで、「自分でアプリを設計して実装する力」が着実に身に付いていきます。

それでは、次のハンズオン記事でお会いしましょう!

いいなと思ったら応援しよう!