8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DOM操作を使った簡易的なtodoリストアプリを作ってみた

Last updated at Posted at 2019-03-01

現在つよぽんさんの元でフロントエンド、バックエンドについて教わっています。今回はその中の課題の1つ「【エクササイズ】DOM操作をしてTodoリストを作成する【JavaScript】」に取り組んだ際の流れ、コードの説明をしていきたいと思います。

つよぽんさんのTwitterアカウント
学習サイト【Web白熱教室】
MENTA

制作物の完成サンプル

github


必要な機能

今回は各挙動を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 = [];

入力したタスクをこちらに配列として保存します。

DOM要素を用いてID値を会得する
const textBox = document.getElementById('input-todo-box');
const addButton = document.getElementById('add-button');
const todoListElement = document.getElementById('todo-list');

DOM操作に用いるために必要な要素をdocument.getElementByIdで取得します。
今回必要なのは

  • タスクの入力 → inputタグのidinput-todo-box
  • タスクの追加 → buttonタグのidadd-button
  • タスクのリスト表示 → ulタグのidtodo-list

なので、この3つを取得しました。

Document.getElementById()【MDN】

追加ボタンをクリックした際の挙動(DOM操作)

addButton.addEventListener("click", event => {
  const todo = textbox.value;
  textbox.value = "";
  if(todo && todo.match(/\S/g)){
    todos.push(todo);
    showTodo();
  }
});

addEventListeneraddButtonをクリックした際の挙動を設定します。

EventTarget.addEventListener()【MDN】

まず変数todoにテキストボックスに入力した文字をvalueプロパティを用いて代入します。
その後、入力した後のテキストボックスは空にしなければなりませんので、""を代入します。

また、テキストボックス内が空のままだったり、スペース入力でそのまま空のタスクがリストに追加されるのはよくないですので、ifを使い、todoに文字が入っている場合にのみリストに追加されるようにします。
ifの中で、先に作った定数todosの配列にtodoを追加します。

しかしこれではHTML上にタスクが表示されることがありません。なので、その後HTML上にタスクを表示する関数showTodoを宣言し、タスクが追加されるたびに実行されるようにします

todosの中身等をHTML上に表示するための関数
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を足します。

Node.textContent【MDN】

Document.createElement【MDN】

デリートボタン、それとデリートボタンがクリックされた際の挙動もここで作成します。(入力したタスクに結び付けないといけないため)
button要素をcreateElementで作成して、デリートボタンがクリックされた際、deleteTodoを実行するようにします。

最後に、liElementとdeteleButtonをtodoListElementにappendChildで追加します。これで追加したタスクがページ上に表示されるようになります。

Node.appendChild【MDN】

todoの削除
const deleteTodo = index => {
  todos.splice(index, 1);
  showTodo();
};

最後にdeleteButtonをクリックした際に宣言されるdeleteTodoの作成です。
spliceを使い、インデックス値から1つ目の値、つまりdeleteButtonと結び付けられているタスク(liElement)を削除し、配列の中身を変更します。そして再度showTodoを宣言してタスク一覧を表示させます。今回は課題として関数を別で作るようにしましたが、直接addEventListenerに記述しても問題なく動作します。

Array.prototype.splice()【MDN】


つまづき

タスクを入力すると重複して表示される。

これはshowTodoで最初のwhile文を記述してなかったために起きた症状です。todoListElement内にどんどん溜まっていく一方ですから当たり前ですね。

while文の中身は、firstchildでtodoListElement内にタスクが入ってる(true)ことを判別した場合、removechildでtodoListElement内のfirstChildを削除し続けるループ処理を実行しています

Node.firstChild【MDN】

Node.removeChild【MDN】

削除ボタンを押したら必ず1番目のタスクが削除される。

単純にindex値の渡し忘れでした。

liElementとdeleteButtonを別々にtodoListElementに追加した場合改行される

todoListElement.appendChild(liElement);
todoListElement.appendChild(deleteButton);

とすると、タスクとボタンの間で改行が発生しました、この追加方法だとHTMLに直すと

<ul>
  <li>...</li>
  <button>...</button>
</ul>

このように、ulの中にlibuttonが別々に追加されるので、改行が発生します。なので

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' にマッチします。

とのことです。

正規表現【MDN】


今後してみたいこと

CSSを使った装飾

現状の見た目だとかなり味気ないので、webサイトとして見れる形にしたい。

ユーザー登録機能を使って個々にtodoを作れるようにする

上記に加え、Webアプリとして機能するようにしてみたい。


みていただきありがとうございました

8
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?