メインコンテンツまでスキップ

プロジェクト

ここまでの知識を使って、シンプルなToDoアプリを作ってみましょう。

ルール

  • ToDoの追加ができます。
  • ToDoの削除ができます。
  • ToDoの編集ができます。
  • 入力欄が空欄だとToDoの追加ができなくなります。

手順

いきなり作るのが難しい場合はタスクを分解してみましょう。今回はルールにある4つの課題をひとつずつ解決していきます。

  1. ToDoを追加できるようにする。
  2. ToDoを削除できるようにする。
  3. ToDoを編集できるようにする。
  4. 空のToDoを追加できないようにする。

の4つの仕事があるので、まず1からやっていきましょう。

ステップ1: ToDoを追加する

ひとまずToDoの一覧、ToDoの入力欄、ToDoの追加ボタンが必要です。ul要素とinput要素とbutton要素を使ってみましょう。

index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Title</title>
</head>
<body>
<ul id="todo-list"></ul>
<input id="todo-input" />
<button id="add-button">追加</button>
<script src="./script.js"></script>
</body>
</html>

次にJavaScriptファイルを作成して追加ボタンを押したときの処理を記述しましょう。

script.js
const todoList = document.getElementById("todo-list");
const todoInput = document.getElementById("todo-input");
const addButton = document.getElementById("add-button");

addButton.onclick = () => {
// 追加ボタンを押すとToDoを1つ追加する
};

このとき、箇条書きの項目として新たにli要素を追加する必要があります。DOMの章で扱ったように、document.createElement関数を使うと新しい要素を作成できます。また、Node#appendChildメソッドを用いることで既存の要素内に子要素を追加することができます。

const li = document.createElement("li");
todoList.appendChild(li);

また、HTMLInputElement#valueプロパティから、DOMに対応するinput要素の入力欄への入力内容を取得できます。

const todoText = document.createElement("span");
todoText.textContent = todoInput.value; // input要素に入力された文字列を取得する
todoInput.value = ""; // input要素の入力欄を空にする
解答例: ステップ 1
index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Title</title>
</head>
<body>
<ul id="todo-list"></ul>
<input id="todo-input" />
<button id="add-button">追加</button>
<script src="./script.js"></script>
</body>
</html>
script.js
const todoList = document.getElementById("todo-list");
const todoInput = document.getElementById("todo-input");
const addButton = document.getElementById("add-button");

addButton.onclick = () => {
const todoItem = document.createElement("li");
const todoText = document.createElement("span");
todoText.textContent = todoInput.value;
todoInput.value = "";
todoItem.appendChild(todoText);
todoList.appendChild(todoItem);
};

ステップ2: ToDoを削除する

次に削除ボタンを追加します。document.createElement関数でbutton要素を生成し削除ボタンにしてからNode#appendChildメソッドで要素内に追加しましょう。

削除ボタンを押すとli要素が1つ消える機能を実装するためには、ある要素から子要素を取り除く Node#removeChildメソッドを使います。

// todoListからtodoItemを取り除く
todoList.removeChild(todoItem);
解答例: ステップ2
index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Title</title>
</head>
<body>
<ul id="todo-list"></ul>
<input id="todo-input" />
<button id="add-button">追加</button>
<script src="./script.js"></script>
</body>
</html>
script.js
const todoList = document.getElementById("todo-list");
const todoInput = document.getElementById("todo-input");
const addButton = document.getElementById("add-button");

addButton.onclick = () => {
const todoItem = document.createElement("li");
const todoText = document.createElement("span");
const deleteButton = document.createElement("button");
todoText.textContent = todoInput.value;
todoInput.value = "";
deleteButton.textContent = "削除";
deleteButton.onclick = () => {
todoList.removeChild(todoItem);
};
todoItem.appendChild(todoText);
todoItem.appendChild(deleteButton);
todoList.appendChild(todoItem);
};

ステップ3 (発展) : ToDoを編集する

編集ボタンをつけてみましょう。

編集ボタンが押されたときに、ユーザーにテキストを入力するように促すダイアログを表示します。prompt関数を使い、ユーザーに入力を求めることができます。

todoText.textContent = prompt("新しい内容を入力してください。"); // ユーザーに入力を求める
解答例: ステップ3
index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Title</title>
</head>
<body>
<ul id="todo-list"></ul>
<input id="todo-input" />
<button id="add-button">追加</button>
<script src="./script.js"></script>
</body>
</html>
script.js
const todoList = document.getElementById("todo-list");
const todoInput = document.getElementById("todo-input");
const addButton = document.getElementById("add-button");

addButton.onclick = () => {
const todoItem = document.createElement("li");
const todoText = document.createElement("span");
const editButton = document.createElement("button");
const deleteButton = document.createElement("button");
todoText.textContent = todoInput.value;
todoInput.value = "";
editButton.textContent = "編集";
editButton.onclick = () => {
todoText.textContent = prompt("新しい内容を入力してください。");
};
deleteButton.textContent = "削除";
deleteButton.onclick = () => {
todoList.removeChild(todoItem);
};
todoItem.appendChild(todoText);
todoItem.appendChild(editButton);
todoItem.appendChild(deleteButton);
todoList.appendChild(todoItem);
};

ステップ4 (発展) : 空のToDoを入れさせない

HTMLButtonElement#disabledプロパティtrueの時、ボタンはクリックを受け付けなくなります。入力欄が空の時にこのプロパティをtrueに、それ以外の時はfalseにすることによって空のタスクの追加を防ぐことができます。この時、入力欄に何かキー入力があるたびに入力欄が空かどうかを判定する必要があります。HTMLElement#oninputプロパティにイベントハンドラを登録すると、ユーザーによって要素に入力されたときに実行される関数を定めることができます。

todoInput.oninput = () => {
// 入力欄が空の時はボタンを押せないようにする
addButton.disabled = todoInput.value === "";
};
解答例: ステップ4
index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Title</title>
</head>
<body>
<ul id="todo-list"></ul>
<input id="todo-input" />
<button id="add-button" disabled>追加</button>
<script src="./script.js"></script>
</body>
</html>
script.js
const todoList = document.getElementById("todo-list");
const todoInput = document.getElementById("todo-input");
const addButton = document.getElementById("add-button");

todoInput.oninput = () => {
addButton.disabled = todoInput.value === "";
};

addButton.onclick = () => {
const todoItem = document.createElement("li");
const todoText = document.createElement("span");
const editButton = document.createElement("button");
const deleteButton = document.createElement("button");
todoText.textContent = todoInput.value;
todoInput.value = "";
addButton.disabled = true; // valueへの代入はoninputイベントを発火しない
editButton.textContent = "編集";
editButton.onclick = () => {
const input = prompt("新しい内容を入力してください");
// prompt関数は入力された文字列が空の場合は空文字列 ("")、キャンセルされた場合はnullを返す
if (input !== "" && input !== null) todoText.textContent = input;
};
deleteButton.textContent = "削除";
deleteButton.onclick = () => {
todoList.removeChild(todoItem);
};
todoItem.appendChild(todoText);
todoItem.appendChild(editButton);
todoItem.appendChild(deleteButton);
todoList.appendChild(todoItem);
};