🎉
メモアプリを改善してみた(dialog + Promise編)
はじめに
以前作ったメモアプリを改善してみました。元のコードではユーザーからの入力をprompt()
で受け取って画面に表示していました。
次のステップアップとして、dialog要素とPromiseを使って作り直してみることにしました。
この記事では、その改善過程をまとめます。
元のコード(prompt版)
元のコードはこちら。
コード全文は以下です。
<h1>メモアプリ</h1>
<button id="addMemoBtn">メモを追加</button>
<ul id="memo_list"></ul>
<script>
const btn = document.getElementById("addMemoBtn");
const memo_list = document.getElementById("memo_list");
btn.addEventListener("click", () => {
const input = prompt("入力してください");
const newElem = document.createElement("li");
newElem.textContent = input;
memo_list.appendChild(newElem);
});
</script>
改良版(dialog + Promise版)
同じ機能をdialog
要素とPromiseで実装した結果です。
以下、改良版のコード全文です。
<h1>メモアプリ</h1>
<button id="addMemoBtn">メモを追加</button>
<ul id="memo_list"></ul>
<dialog id="inputDialog">
<form method="dialog">
<label for="memoInput">入力してください:</label>
<input type="text" id="memoInput">
<div>
<button type="submit" value="ok">OK</button>
<button type="submit" value="cancel">キャンセル</button>
</div>
</form>
</dialog>
<script>
const btn = document.getElementById("addMemoBtn");
const memo_list = document.getElementById("memo_list");
const inputDialog = document.getElementById("inputDialog");
const memoInput = document.getElementById("memoInput");
btn.addEventListener("click", async () => {
memoInput.value = "";
inputDialog.showModal();
const input = await new Promise((resolve) => {
inputDialog.onclose = () => {
if (inputDialog.returnValue === "ok") {
resolve(memoInput.value);
} else {
resolve(null);
}
};
});
if (!input || input.trim() === "") return;
const newElem = document.createElement("li");
newElem.textContent = input;
memo_list.appendChild(newElem);
});
</script>
コード解説
1. dialogを表示する
memoInput.value = "";
inputDialog.showModal();
-
memoInput.value = ""
で前回の入力をクリア -
showModal()
でdialogをモーダル表示(背景が暗くなり、他の操作ができなくなる)
2. dialogが閉じるまで待つ(重要!)
const input = await new Promise((resolve) => {
inputDialog.onclose = () => {
if (inputDialog.returnValue === "ok") {
resolve(memoInput.value);
} else {
resolve(null);
}
};
});
ここが一番重要な部分
まず、Promise
(プロミス)について。
Promiseとは?
Promiseは「将来の結果を約束するオブジェクト」
今回の場合:
- ユーザーが入力中 → 結果はまだ分からない
- OKまたはキャンセルを押す → 結果が確定
- 確定した結果を返す
awaitとは?
await
は「Promiseの結果が出るまで待つ」命令。
const input = await new Promise(...);
この1行で「ユーザーがOKかキャンセルを押すまで待つ」ことができます。まるでprompt()
のように、上から順番に処理が進むように書けるのがポイントです。
各部分の役割
-
new Promise((resolve) => {...})
:「これから結果を返すよ」という約束を作る -
inputDialog.onclose
:dialogが閉じた時に実行される処理 -
inputDialog.returnValue
:押されたボタンのvalue
属性("ok" または "cancel") -
resolve(memoInput.value)
:入力値を結果として返す(「resolve」は「解決する、確定させる」という意味) -
resolve(null)
:キャンセル時はnull
を返す
3. 入力チェック
if (!input || input.trim() === "") return;
以下の場合は何もせず終了します:
- キャンセルされた場合(
input
がnull
) - 空文字の場合(スペースだけの入力も除外)
4. メモをリストに追加
const newElem = document.createElement("li");
newElem.textContent = input;
memo_list.appendChild(newElem);
ここは元のコードと同じです。入力値を<li>
要素に入れてリストに追加しています。
補足:非同期処理について
「Promise」や「await」を理解するには、非同期処理を知っておく必要があります。
プログラムには2種類の処理があります:
同期処理: 順番に1つずつ実行される
console.log("1番目");
console.log("2番目");
console.log("3番目");
// 結果: 1→2→3 の順番
非同期処理: 待ち時間がある処理を後回しにして、他の作業を先に進める
console.log("1番目");
setTimeout(() => {
console.log("2番目(3秒後)");
}, 3000);
console.log("3番目");
// 結果: 1→3→2 の順番(3は2を待たない)
身近な例で説明すると:
レストランでの注文シーン。
- 同期処理:注文した料理が来るまで、その場で立って待つ。何もできない。
- 非同期処理:注文したら席に座って、料理ができるまで他のことができる。料理ができたら呼ばれる。
今回のコードでは:
-
prompt()
:同期処理(入力するまで全ての処理が止まる) -
dialog + Promise
:非同期処理をawait
で同期的に扱う(入力を待つが、書き方は同期的)
await
を使うことで、非同期処理でもprompt()
のように書けるのがポイントです。
今回の改善のメリットとデメリット
メリット ✨
- 見た目を自由にカスタマイズできる(CSSでスタイル変更可能)
- 複数の入力欄やボタンを追加できる
デメリット 🤔
-
コード量が増える(
prompt()
は1行だが、こちらは10行以上。簡単な用途には過剰かも)
今回学んだこと
-
dialog
要素を使えば見た目をカスタマイズできる入力画面を作れる -
Promise
とawait
で、非同期処理を同期的に書ける - 最初は複雑に見えたが、理解できると応用が効きそう
Discussion