現在つよぽんさんの元でフロントエンド、バックエンドについて教わっています。今回はその中の課題の1つ「【エクササイズ】DOM操作をしてTodoリストを作成する【JavaScript】」に取り組んだ際の流れ、コードの説明をしていきたいと思います。
つよぽんさんのTwitterアカウント
学習サイト【Web白熱教室】
MENTA
制作物の完成サンプル
必要な機能
今回は各挙動をDOM操作を用いて実行することとする
- タスク入力欄
- todoに追加したいタスク(文字)を入力する
- 追加ボタン
- クリックすると、入力したタスクがtodoリストに追加される
- todoリスト一覧
- 入力されたタスクを表示する。
- 「
表示される順の番号
:入力したタスク
」と表示する。
- 削除ボタン
- クリックするとタスクを一覧から削除することができる。
- 残ったタスクの
表示される順の番号
はタスクが削除された後の順番に変更されなければならない。
- 残ったタスクの
- クリックするとタスクを一覧から削除することができる。
今回はあくまで純粋なtodoリストを作る課題なので、CSS等の装飾機能はつけない
コード
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>【エクササイズ】Todoリストの作成</title>
</head>
<body>
<h1>Todoアプリ</h1>
<div>
<label for="input-todo-box">Todoタスクの入力 : </label>
<input id="input-todo-box" type="text">
<button id="add-button">追加</button>
</div>
<h2>現在のTodoリスト</h2>
<ul id="todo-list"></ul>
<script src="main.js"></script>
</body>
</html>
Javascript
const todos = [];
入力したタスクをこちらに配列として保存します。
const textBox = document.getElementById('input-todo-box');
const addButton = document.getElementById('add-button');
const todoListElement = document.getElementById('todo-list');
DOM操作に用いるために必要な要素をdocument.getElementById
で取得します。
今回必要なのは
- タスクの入力 → inputタグのid
input-todo-box
- タスクの追加 → buttonタグのid
add-button
- タスクのリスト表示 → ulタグのid
todo-list
なので、この3つを取得しました。
Document.getElementById()【MDN】
addButton.addEventListener("click", event => {
const todo = textbox.value;
textbox.value = "";
if(todo && todo.match(/\S/g)){
todos.push(todo);
showTodo();
}
});
addEventListener
でaddButton
をクリックした際の挙動を設定します。
EventTarget.addEventListener()【MDN】
まず変数todo
にテキストボックスに入力した文字をvalue
プロパティを用いて代入します。
その後、入力した後のテキストボックスは空にしなければなりませんので、""
を代入します。
また、テキストボックス内が空のままだったり、スペース入力でそのまま空のタスクがリストに追加されるのはよくないですので、if
を使い、todoに文字が入っている場合にのみリストに追加されるようにします。
ifの中で、先に作った定数todos
の配列にtodo
を追加します。
しかしこれではHTML上にタスクが表示されることがありません。なので、その後HTML上にタスクを表示する関数showTodo
を宣言し、タスクが追加されるたびに実行されるようにします
const showTodo = () => {
// HTML上の表示を一旦リセットする
while(todoListElement.firstChild) {
todoListElement.removeChild(todoListElement.firstChild);
}
todos.forEach((todo, index) => {
// ulに追加するためにliタグの作成、todos内の配列を代入
const liElement = document.createElement("li");
liElement.textContent = `${index + 1} : ${todo}$`;
// デリートボタンの作成
const deleteButton = document.createElement("button");
deleteButton.textContent = "削除";
//deleteButtonをクリックした際の挙動
deleteButton.addEventListener("click", event => {
deleteTodo(index);
});
//作成した要素をHTML上に表示させる
liElement.appendChild(deleteButton);
todoListElement.appendChild(liElement);
});
};
上のDOM操作で宣言したshowTodo
を作成します。
まず最初に一旦HTML上に表示されているタスクを全て削除。なぜ消すか理由は後述します。
次にforEach
でtodosをイテレーション処理させ、HTML上にタスクが表示されるようにします。
まず、todoListElement
に渡されているIDはtodo-list
、IDが入っているタグ要素はul
ですね。ul
に入る要素はli
、なのでliをdocument.createElement
で作成、変数に代入し、textContent
プロパティでtodos内の値を代入します。index値そのままを代入すると0からのスタートになるので、1を足します。
デリートボタン、それとデリートボタンがクリックされた際の挙動もここで作成します。(入力したタスクに結び付けないといけないため)
button
要素をcreateElement
で作成して、デリートボタンがクリックされた際、deleteTodo
を実行するようにします。
最後に、liElementとdeteleButtonをtodoListElementにappendChild
で追加します。これで追加したタスクがページ上に表示されるようになります。
const deleteTodo = index => {
todos.splice(index, 1);
showTodo();
};
最後にdeleteButtonをクリックした際に宣言されるdeleteTodo
の作成です。
splice
を使い、インデックス値から1つ目の値、つまりdeleteButtonと結び付けられているタスク(liElement)を削除し、配列の中身を変更します。そして再度showTodoを宣言してタスク一覧を表示させます。今回は課題として関数を別で作るようにしましたが、直接addEventListenerに記述しても問題なく動作します。
つまづき
タスクを入力すると重複して表示される。
これはshowTodo
で最初のwhile文を記述してなかったために起きた症状です。todoListElement
内にどんどん溜まっていく一方ですから当たり前ですね。
while文の中身は、firstchild
でtodoListElement内にタスクが入ってる(true)ことを判別した場合、removechild
でtodoListElement内のfirstChild
を削除し続けるループ処理を実行しています
削除ボタンを押したら必ず1番目のタスクが削除される。
単純にindex値の渡し忘れでした。
liElementとdeleteButtonを別々にtodoListElementに追加した場合改行される
todoListElement.appendChild(liElement);
todoListElement.appendChild(deleteButton);
とすると、タスクとボタンの間で改行が発生しました、この追加方法だとHTMLに直すと
<ul>
<li>...</li>
<button>...</button>
</ul>
このように、ul
の中にli
とbutton
が別々に追加されるので、改行が発生します。なので
liElement.appendChild(deleteButton);
todoListElement.appendChild(liElement);
こうすると
<ul>
<li>...<button>...</button></li>
</ul>
li
の中にbutton
を追加した後、ul
に追加しているので、改行されずに表示されます。
@tsuyopon_xyz さんありがとうございます!
テキストボックスにスペース入力した後追加をすると空のタスクがリストに追加される
todo.match(/\S/g)
で解決しました、意味はよくわかりませんが、
\S
スペース以外の文字にマッチします。 [^ \f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] に相当します。
例えば /\S\w*/ は "foo bar" の 'foo' にマッチします。
とのことです。
今後してみたいこと
CSSを使った装飾
現状の見た目だとかなり味気ないので、webサイトとして見れる形にしたい。
ユーザー登録機能を使って個々にtodoを作れるようにする
上記に加え、Webアプリとして機能するようにしてみたい。
みていただきありがとうございました