JavaScriptでToDoリストを作ってみた ローカルストレージ編【第11話】

フロントエンド学習記録
この記事は約35分で読めます。

こんにちは!みさんです。
今回は、以前作ったToDoリストにローカルストレージを導入していきます。

前回のお話はこちら↓

ToDoリストおさらい

タスクの登録・完了・削除ができるようになっていますが、リロード(再読み込み)すると全て消えてしまいます。

そこで今回はローカルストレージを導入して、再読み込みをしても消えないように実装していきます。
※本記事ではローカルストレージとは?については割愛します。

ローカルストレージの導入

ローカルストレージに保存する準備

まずは登録ボタンを押したらローカルストレージに保存されるようにしてみます。
コードの冒頭に以下2行を追加します。

let listItems = [];
const storage = localStorage;

let listItems = []; でデータを入れる箱を用意しています。そして2行目はlocalStorageというオブジェクトを定数に入れて扱いやすくしています。
この記述をしても何も変わりません。

登録を押したらローカルストレージに保存

保存する準備は整ったので、登録を押したときの処理にローカルストレージに保存する処理を追加します。
「//以下を追加」から下を登録処理のif文の中の冒頭に記述します。

todoRegister.addEventListener('click', () => {
  if (todoValue.value !== '') {
   //以下を追加
   const item = { //①
    todoValue: todoValue.value, //②
    isDone: false, //③
    isDeleted: false //④
  };
   listItems.push(item); //⑤
   storage.store = JSON.stringify(listItems); //⑥

① const item = {};
1つ1つのタスクのデータをオブジェクト形式で保存する箱を用意します。
※今回は配列の中にオブジェクトで保存する方法で進めます。
[ { prop1: val1, prop2: val2, prop3: val3 } ] という形式です。

② todoValue: todoValue.value,
②③④は用意したオブジェクトの箱(①)にプロパティと値を指定して保存の準備をします。
まずtodoValueというプロパティ名で、todoValue.value(=inputに入力した文字列)を値として保存します。
※左辺todoValueはオブジェクト内のプロパティ名。右辺todoValueはタスクを入力するinput要素の取得のための定数。(下記参照)

console.log(item.todoValue);
ローカルストレージに保存したデータ配列>オブジェクト内のtodoValueの値

console.log(todoValue);
タスクを入力するinput要素

console.log(todoValue.value);
inputに入力された文字列

③ isDone: false,
次にisDoneというプロパティに、falseを保存します。このプロパティは完了したかどうかの判断に使います。

④ isDeleted: false
最後にisDeletedというプロパティにfalseを保存します。このプロパティは削除したかどうかの判断に使います。

⑤ listItems.push(item);
②③④で保存準備したデータを配列 listItems に.pushで配列要素を追加します。

⑥ storage.store = JSON.stringify(listItems);
追加した配列要素をローカルストレージにstoreキーで保存します。
このJSON.stringifyメソッドを使うことで、配列やオブジェクトをJSONに変換してくれます。

ここまで実装するとローカルストレージに保存されるようになります。

ローカルストレージはデベロッパーツールのアプリケーションタブから確認することができます。
タスクを登録すると、タスク名、完了かどうか、削除かどうかの値を持ったデータが保存されたことがわかります。
ただまだ保存しただけなので、リロードをすると画面上では消えてしまいます。次にリロードしてもデータを画面上に保持するように実装しましょう。

リロードでローカルストレージのデータを表示

まずはリロードした時のイベント登録の雛形です。

document.addEventListener("DOMContentLoaded", () => {
  //処理を書く
 console.log("リロードされました!")
});

この記述を追加した上でリロードすると、コンソールに「リロードされました!」と表示されるはずです。では、ローカルストレージに保存しているデータが表示されるように実装していきます。

document.addEventListener("DOMContentLoaded", () => {
  const json = storage.store; //①
  if (json === undefined) { //②
    return;
  }
  listItems = JSON.parse(json); //③

  for (const item of listItems) { //④
    //処理は後述
  }
});

① const json = storage.store;
ローカルストレージにstoreキーで保存しているデータをjsonという定数に入れておきます。

② if (json === undefined) { return; }
ローカルストレージにデータがないときは、returnでイベントを終了させるよう記述します。

③ listItems = JSON.parse(json);
取得したローカルストレージのデータがJSON形式なので、配列やオブジェクト形式に戻します。
先ほどローカルストレージに保存したときに記述した、JSON.stringifyと逆の変換で、JSON形式を配列やオブジェクトに変換してくれます。

④ for (const item of listItems) { /*処理は後述*/ }
for of文で配列データ(listItems)を1つ1つ取り出して、それを item という定数に入れて繰り返しの処理を行う準備をします。

次に、繰り返しの処理を書いていきます。ここは以前行った登録ボタンを押した時のイベント処理とほとんど同じです。

    const litag = document.createElement("li");
    const ptag = document.createElement("p");

    //ul>li>p構造を作る
    ptag.appendChild(todo);
    litag.appendChild(ptag);
    todoList.appendChild(litag);

    //ボタンを入れるdiv要素追加
    const btn_box = document.createElement("div");
    btn_box.setAttribute("class", "btn-box");
    litag.appendChild(btn_box);

    //完了ボタン追加
    const donebtn = document.createElement("button");
    donebtn.setAttribute("id", "js-done-btn");
    donebtn.innerHTML = "完了";
    btn_box.appendChild(donebtn);

    //削除ボタン追加
    const delbtn = document.createElement("button");
    delbtn.setAttribute("id", "js-del-btn");
    delbtn.innerHTML = "削除";
    btn_box.appendChild(delbtn);

    //削除機能追加
    delbtn.addEventListener("click", () => {
      deleteTodo(delbtn);
    });

    //完了機能追加
    donebtn.addEventListener("click", () => {
      doneTodo(donebtn);
    });

そしてコピペをしたコードの上に、const todo = document.createTextNode(item.todoValue); を追記します。
登録処理の中にもありますが、( )内が異なります。登録処理の時は、inputに入力された文字列を取得しましたが、今回はローカルストレージのデータ内の文字列を取得します。
for of文でデータを一度 itemに入れています。なのでitem.todoValueとすることでタスクの文字列を取得できます。

document.addEventListener("DOMContentLoaded", () => {
  const json = storage.store;
  if (json === undefined) {
    return;
  }
  listItems = JSON.parse(json);

  for (const item of listItems) {
    const todo = document.createTextNode(item.todoValue);

    const litag = document.createElement("li");
    const ptag = document.createElement("p");

    //ul>li>p構造を作る
    ptag.appendChild(todo);
    litag.appendChild(ptag);
    todoList.appendChild(litag);

    //ボタンを入れるdiv要素追加
    const btn_box = document.createElement("div");
    btn_box.setAttribute("class", "btn-box");
    litag.appendChild(btn_box);

    //完了ボタン追加
    const donebtn = document.createElement("button");
    donebtn.setAttribute("id", "js-done-btn");
    donebtn.innerHTML = "完了";
    btn_box.appendChild(donebtn);

    //削除ボタン追加
    const delbtn = document.createElement("button");
    delbtn.setAttribute("id", "js-del-btn");
    delbtn.innerHTML = "削除";
    btn_box.appendChild(delbtn);

    //削除機能追加
    delbtn.addEventListener("click", () => {
      deleteTodo(delbtn);
    });

    //完了機能追加
    donebtn.addEventListener("click", () => {
      doneTodo(donebtn);
    });
  }
});

これでリロードをしても画面から消えることはなくなりました。
しかし、完了を押したり削除をしてもその処理はリロードをすると取り消されてしまいます。次は完了や削除状態を保存する実装をします。

完了や削除状態を保存する

完了状態を保存する

まずは完了を保存する実装をします。タスク登録時にローカルストレージにdoneというプロパティを保存しました。完了ボタンを押したらdoneプロパティの値をtrueにする実装をします。

const doneTodo = (donebtn) => {
  const doneTodo = donebtn.closest("li");
  doneTodo.setAttribute("class", "done-item");
  doneList.appendChild(doneTodo);

  //以下を追記
  const donebtnDiv = donebtn.closest("div"); //①
  const doneTodoTxt = donebtnDiv.previousElementSibling; //②
  const doneValue = listItems.find( //③
    (item) => item.todoValue === doneTodoTxt.textContent
  );
  doneValue.isDone = true; //④
  storage.store = JSON.stringify(listItems); //⑤
  //追記ここまで

  donebtn.remove();
};

① const donebtnDiv = donebtn.closest(“div”);
押された完了ボタンから1番近いdiv要素を取得します。

② const doneTodoTxt = donebtnDiv.previousElementSibling;
previousElementSiblingはdonebtnDiv取得してきたdiv要素の前の要素(今回はpタグ)を取得できます。

③ const doneValue = listItems.find( (item) => item.todoValue === doneTodoTxt.textContent );
listItems配列の中から.findで( )内の処理を行い一致するオブジェクトを探して定数に入れます。
item.todoValue(オブジェクトのtodoValueプロパティの値)とdoneTodoTxt.textContent(②で取得したpタグの文字列)が一致するオブジェクトを探します。
※同じ文字列のタスクを複数登録するとうまく動きません。同一文字列のタスク登録を禁止する処理はこのあと行います。

④ doneValue.isDone = true;
③で取得したオブジェクトデータのisDoneプロパティをtrueに変更します。

⑤ storage.store = JSON.stringify(listItems);
ローカルストレージに保存します。

以上の追記をすると完了を押したときに、アプリケーションタブのstoreキーで保存している該当データのdoneがtrueに変わっているはずです。

変わりましたね。ただまだ完成ではありません。今度はリロード時にオブジェクトデータのisDoneプロパティの値によって、ToDoボックスの中に出力か、Doneボックスの中に出力を分ける必要があります。

先程リロード時に作ったイベント処理の中に条件分岐を書いて修正していきます。

まず黄色で囲われたコードは、isDoneがfalse=タスク未完了の場合の処理にするため if(item.isDone === false)文内に移動させます。(追記:画像内の「item.done === false」から更新)

そして緑で囲われたコードを含むif文は、タスクが未完了か完了かによってどちらのリストに入れるかの条件分岐です。
黄色枠コードのif文同様に、if(item.isDone === false)と条件を置き、当てはまるのならばToDoリスト(未完了リスト)に入れる処理をするため、緑枠コードを移動しています。
そしてelse(item.isDone === true)のときの処理は、doneList(完了リストのul要素を取得する定数)に.appendchildで入れています。そして完了タスクには、done-itemというクラスを付与するのでlitag.setAttribute(“class”, “done-item”)でdone-itemクラスを付与します。

  for (const item of listItems) {
    const todo = document.createTextNode(item.todoValue);

    const litag = document.createElement("li");
    const ptag = document.createElement("p");

    //ul>li>p構造を作る
    ptag.appendChild(todo);
    litag.appendChild(ptag);

    if (item.isDone === false) {
      todoList.appendChild(litag);
    } else {
      doneList.appendChild(litag);
      litag.setAttribute("class", "done-item");
    }

    //ボタンを入れるdiv要素追加
    const btn_box = document.createElement("div");
    btn_box.setAttribute("class", "btn-box");
    litag.appendChild(btn_box);

    if (item.isDone === false) {
      //完了ボタン追加
      const donebtn = document.createElement("button");
      donebtn.setAttribute("id", "js-done-btn");
      donebtn.innerHTML = "完了";
      btn_box.appendChild(donebtn);
      //完了機能追加
      donebtn.addEventListener("click", () => {
        doneTodo(donebtn);
      });
    }

    //削除ボタン追加
    const delbtn = document.createElement("button");
    delbtn.setAttribute("id", "js-del-btn");
    delbtn.innerHTML = "削除";
    btn_box.appendChild(delbtn);

    //削除機能追加
    delbtn.addEventListener("click", () => {
      deleteTodo(delbtn);
    });
  }

以上までを記述することで、やっと完了状態の保存ができるようになりました!

削除状態を保存する

続いて削除状態を保存し、リロードをしても画面に出てこないように実装します。
手順としてはローカルストレージのisDeletedプロパティの値をtrueに変更→filterで削除状態以外のものを新しい配列として取得→ローカルストレージに保存という流れです。

const deleteTodo = (delbtn) => {
  const delconfirm = this.confirm("本当に削除しますか?");
  if (delconfirm === true) {
    const choseTodo = delbtn.closest("li");
    if (choseTodo.classList.contains("done-item")) {
      doneList.removeChild(choseTodo);
    } else {
      todoList.removeChild(choseTodo);
    }

    //以下を追記
    const delbtnDiv = delbtn.closest("div"); //①
    const delTodoTxt = delbtnDiv.previousElementSibling; //②
    const delValue = listItems.find( //③
      (item) => item.todoValue === delTodoTxt.textContent
    );
    delValue.isDeleted = true; //④
    const newlistItems = listItems.filter((item) => item.isDeleted === false); //⑤
    listItems = newlistItems; //⑥
    storage.store = JSON.stringify(listItems); //⑦
    //追記ここまで

  }
};

①〜④と⑦については、定数名を done〇〇からdel〇〇と書き換えているだけで、完了のときと同じなので説明は割愛します。

⑤ const newlistItems = listItems.filter((item) => item.isDeleted === false);
.filterメソッドを使って、isDeletedプロパティがfalseのデータを取得し定数に入れます。

⑥ listItems = newlistItems;
⑤で取得した削除されていないデータだけが残った配列をlistItemに入れています。

ここまでを実装すると削除したデータはローカルストレージからも消えて、リロードしても画面にも出てきません。
もし削除したタスクを戻したいときは別の実装が必要になります。

同一文字列のタスク登録を禁止する

先程完了のときに触れましたが、このままだと同一文字列のタスクが登録されたときに不具合を起こします。
原因は、完了や削除の保存をするときに.findでtodoValueの値が一致するisdoneやisDeletedプロパティを操作するためです。

同じ文字列のタスクがあると、ここで該当タスク以外の同一文字列タスクのプロパティの値まで変わってしまいます。
そこで今回は同一文字列のタスク登録を禁止する実装を行います。

todoRegister.addEventListener("click", () => {
  if (todoValue.value !== "") {

    //以下を追記
    const registeredValue = listItems.find( //①
      (item) => item.todoValue == todoValue.value
    );

    if (registeredValue === undefined) { //②
      //すでに記述していたプログラム(要素の挿入やボタンの機能追加など)
    } else { //③
      alert(
        "タスクが重複しているため登録できません。別の名前で登録してください。"
      );
    }
    //追記ここまで

  }
});

//すでに記述していたプログラム(要素の挿入やボタンの機能追加など)の部分は長いので割愛しています。

① const regiValue = listItems.find( (item) => item.todoValue == todoValue.value );
listItemsから登録しようとしているタスクと同一文字列のものがすでに保存されているかを探します。
もしある場合は、そのデータが取得されますが、ない場合はundefinedが返ってきます。

② if (registeredValue === undefined) { //すでに記述していたプログラム }
①で返ってきたのがundefinedならば、「//すでに記述していたプログラム」(要素を作って挿入したり、ボタン機能を追加するプログラム)を実行するようにします。
※すでに記述していたプログラムは新しく書くのではなく、if文の中に移動させます。

③ else { alert( “タスクが重複しているため登録できません。別の名前で登録してください。” ); }
①で返ってきたのがundefinedではない場合(=すでに登録されているタスク名)ならば、alertを出して登録しないようにします。

ここまでを実装すると以下のようにアラートを出し登録できないようになります。

おまけ

ここからはおまけとして、完了タスクを未完了に戻せるようにしたり、全データの一括削除ができるようにしたり、コードのリファクタリング(何回も同じようなことを書いている部分を整理して短くする)を行っていきます。

戻すボタンの実装

今回は完了を押したら完了ボタンが消えるように実装していますが、これを完了←→戻すとトグル式(完了を押すと戻すになって、戻すを押すと完了になる仕組み)に実装していきます。

トグルするものとしては完了←→戻すの文字、idDoneプロパティの値の2つで、押したときの処理はプロパティの値を条件に分岐させる流れにします。

まず検索置換(完全一致)で「donebtn」→「togglebtn」に、「doneTodo」→「toggleTodo」に変更します。
そして完了ボタンを押したときの処理関数に以下の条件分岐を追記します。

const toggleTodo = (togglebtn) => {
  const toggleTodo = togglebtn.closest("li");

  //以下を追記
  if (togglebtn.innerHTML === "完了") {

  } else if (togglebtn.innerHTML === "戻す") {

  } else {
    alert("正常に処理できませんでした。リロードをお試しください。");
  }
  //追記ここまで

  //残りの記述は割愛
};

条件分岐の枠ができたら中の処理を移動やコピペで実装していきます。

if文内もelse if文内も、ピンク枠コード→.innerHTMLでボタン文字替え→黄色枠コードの順番になっています。
すでに記述していたコードを移動したりコピペしたりすることで、簡単に実装できますが、画像else if文内の赤枠は変更する必要があります。(追記:プロパティ名の変更を行ったため、画像内一部異なる箇所があります。正しいコードは下記「完了or戻るボタン機能コード」をご覧ください)

const toggleTodo = (togglebtn) => {
  const toggleTodo = togglebtn.closest("li");

  if (togglebtn.innerHTML === "完了") {
    toggleTodo.setAttribute("class", "done-item");
    doneList.appendChild(toggleTodo);
    togglebtn.innerHTML = "戻す";

    const donebtnDiv = togglebtn.closest("div");
    const doneTodoTxt = donebtnDiv.previousElementSibling;
    console.log(doneTodoTxt);
    const doneValue = listItems.find(
      (item) => item.todoValue === doneTodoTxt.textContent
    );
    doneValue.isDone = true;
    storage.store = JSON.stringify(listItems);
  } else if (togglebtn.innerHTML === "戻す") {
    toggleTodo.setAttribute("class", "");
    todoList.appendChild(toggleTodo);
    togglebtn.innerHTML = "完了";

    const donebtnDiv = togglebtn.closest("div");
    const doneTodoTxt = donebtnDiv.previousElementSibling;
    console.log(doneTodoTxt);
    const doneValue = listItems.find(
      (item) => item.todoValue === doneTodoTxt.textContent
    );
    doneValue.isDone = false;
    storage.store = JSON.stringify(listItems);
  } else {
    alert("正常に処理できませんでした。リロードをお試しください。");
  }
};

これでボタンの文字やプロパティの値を変えたり、場所を移動させたりできましたが、リロードをすると戻すボタンが出力されないので、リロード処理に条件分岐を書いて戻すボタンが出るように実装しましょう。

まずピンク枠は、ボタンを作るコードですがこれは条件に関係なく作るのでif文の外(上)に出します。
if文の中では、水色枠のボタンの文字列を完了にするか戻すにするかの処理を行います。
そして黄色枠コードも条件に関係なく、ボタンの挿入や機能追加を行うので、if文の外(下)に出します。
※画像左の白取り消し線を引っ張ったコードは使わないので削除しました。

ここまで実装するとリロードをしても戻すボタンが保持されます。

const togglebtn = document.createElement("button");
if (item.done === false) {
  //完了ボタン追加
  togglebtn.innerHTML = "完了";
} else {
  //完了ボタン追加
  togglebtn.innerHTML = "戻す";
}
btn_box.appendChild(togglebtn);
//完了or戻す機能追加
togglebtn.addEventListener("click", () => {
  toggleTodo(togglebtn);
});

全データの一括削除

現在、ローカルストレージに保存しているデータを一括ですべて消すためには、ディベロッパーツールのアプリケーションタブ→storeキーを右クリックして削除とする必要があります。
この動作を画面上でできるように実装します。

まず、htmlファイルに「全タスク削除」のボタンを設置します。

<div class="register-box" id="js-add-form">
  <input id="js-todo-ttl" type="text" placeholder="タスク名" maxlength="25">
  <button id="js-register-btn">登録</button>
  <!--以下を追加-->
  <button id="js-allremove-btn">全タスク削除</button>
  <!--追加ここまで-->
</div>

そしてこの要素を取得する定数を記述します。記述場所は、script.js冒頭で要素を取得する定数をまとめている部分です。

const todoAllRemove = document.getElementById("js-allremove-btn"); //すべてのタスクを削除するボタン取得

それではこのボタンにイベントを追加していきます。

todoAllRemove.addEventListener("click", () => {
  const allrmconfirm = this.confirm( //①
    "【重要!!】本当にすべてのタスクを削除しますか?削除したデータは復元できません。"
  );
  if (allrmconfirm) {
    delete storage.store; //②
    location.reload(); //③
  }
});

登録ボタンのときと同様に.addEventListenerで「全タスク削除」クリック時のイベントを登録します。

① const allrmconfirm = this.confirm( “【重要!!】本当にすべてのタスクを削除しますか?削除したデータは復元できません。”);
まず、間違えて押したときに戻れるよう、警告文を出します。

② delete storage.store;
もし①がtrueだったら(アラートでOKが押されたら)storageのstoreキーデータをdeleteで削除します。

③ location.reload();
ローカルストレージのstoreを削除しただけでは画面には残るので、リロードを強制的に行って画面からも消すようにします。

これで「全タスク削除」を押すとすべてのタスクが画面からも、ローカルストレージからも削除できます。

コードのリファクタリング

ここまで書いてきたプログラムの中で重複しているものや、似たようなコードになっているところを関数を作成して短くなるようにしていきます。
現在の総コード行は「189行」(コメント含む)です。

まずリファクタリングを行うのは、リロードした時と登録ボタンを押したときにタスクリストを作るプログラムです。見比べてみましょう。

リロード時のイベント

登録ボタンを押したときのイベント

黄色枠部分が似ています。リロードイベントの方に条件分岐が入っていますが、登録時のイベントで使っても、先にローカルストレージに保存するプログラムが実行されるので問題はありません。

リロードのイベントをもとに関数を作ります。先に登録イベントの黄色枠は削除し、リロードイベントの黄色枠をカットしておきます。
(追記:一部コードの可読性を上げるため、if文から三項演算子に修正している箇所があります。)

const createTodoList = (item) => {
  const todo = document.createTextNode(item.todoValue);
  const litag = document.createElement("li");
  const ptag = document.createElement("p");

  //ul>li>p構造を作る
  ptag.appendChild(todo);
  litag.appendChild(ptag);

  if (item.isDone === false) {
    todoList.appendChild(litag);
  } else {
    doneList.appendChild(litag);
    litag.setAttribute("class", "done-item");
  }

  //ボタンを入れるdiv要素追加
  const btn_box = document.createElement("div");
  btn_box.setAttribute("class", "btn-box");
  litag.appendChild(btn_box);

  const togglebtn = document.createElement("button");

 //ボタンのラベルを三項演算子で判断・実行
  const togglebtnLabel = () =>
    item.isDone === false
      ? (togglebtn.innerHTML = "完了")
      : (togglebtn.innerHTML = "戻す");
  togglebtnLabel();

 //完了or戻すボタンをbtn_boxの子要素に挿入
  btn_box.appendChild(togglebtn);

  //完了or戻す機能追加
  togglebtn.addEventListener("click", () => {
    toggleTodo(togglebtn);
  });

  //削除ボタン追加
  const delbtn = document.createElement("button");
  delbtn.setAttribute("id", "js-del-btn");
  delbtn.innerHTML = "削除";
  btn_box.appendChild(delbtn);

  //削除機能追加
  delbtn.addEventListener("click", () => {
    deleteTodo(delbtn);
  });
}

アロー関数でcreateTodoListと名前を付けて、itemを引数に指定した状態で中にカットしたプログラムを貼り付けます。

そしてカットしたり削除したところに以下を追記すれば、問題なく動き始めます。

createTodoList(item);
//登録イベントの方には以下を追記
todoValue.value = "";

まず1つ目のリファクタリングは終了です。
続いて、ローカルストレージのプロパティの値を変更するプログラムをリファクタリングします。

プロパティの値を変更するプログラムは完了or戻すを押したとき、削除を押したときの3箇所で使われています。まずは見比べてみましょう。

黄色枠コードが似ています。異なるのは、定数名や押されたボタン、プロパティ名、変更する値の4点ですね。(追記:プロパティ名の更新に伴い、画像内一部が異なります。 .done→.isDone、.delete→.isDeleted)
定数名については、統一して問題はないので関数を作る際に作成します。
押されたボタンとプロパティ名、値は引数という形で関数を作成します。

最後の保存までまとめたいですね。削除時だけ.filter処理を挟むのでそこを条件分岐にして.filter処理を挟むかスルーかの処理にしましょう。

const updateValue = (btnName, property, value, filter) => { //①
  const getParent = btnName.closest("div"); //②
  const todoTxt = getParent.previousElementSibling; //③
  const changeValue = listItems.find( //④
    (item) => item.todoValue == todoTxt.textContent
  );
  changeValue[property] = value; //⑤
  if (filter) { //⑥
    const newlistItems = listItems.filter((item) => item[property] !== true);
    listItems = newlistItems;
  }
  storage.store = JSON.stringify(listItems); //⑦
};

① const updateValue = (btnName, property, value, filter) => {
アロー関数を用いてカッコ内で引数を指定しています。
btnNameはボタンの名前です。1番近くのdiv要素を取得する際の基準に使います。
propertyはその通りプロパティ名です。
valueは変更するプロパティの値を入れます。
filterは.filterを使って配列を操作する必要があるかないかの条件で使います。

② const getParent = btnName.closest(“div”);
定数名を親要素を取得するということでgetParentとし引数のbtnNameから1番近いdivタグを取得します。

③ const todoTxt = getParent.previousElementSibling;
タスクの文字列を取得するということで、todoTxtとし②で取得した要素の前の要素(pタグ要素)を取得します。

④ const changeValue = listItems.find( (item) => item.todoValue == todoTxt.textContent);
プロパティの値を変えるデータを探すコードです。定数名をchangeValueとした以外は変更ありません。

⑤ changeValue[property] = value;
プロパティの値を変えるコードです。④で取得したデータの値を変更したいプロパティを「property」引数とし、値を「value」引数にしています。

⑥ if (filter) {
const newlistItems = listItems.filter((item) => item[property] !== true);
listItems = newlistItems;
}

if(filter) で引数にtrueを入れたときだけ実行されるプログラムです。
削除時の.filterで新たな配列を作るときの処理です。item.deleteとしていたところをitem[property]に変更しています。このpropertyも引数で指定します。

⑦ storage.store = JSON.stringify(listItems);
最後に変更したデータをローカルストレージに保存して終了です。

ひと通り関数は作れたので、呼び出してみましょう。
先程見比べたときのコードの上に以下を追記します。

//削除を押した時
updateValue(delbtn, "isDeleted", true, true);
//完了を押した時
updateValue(togglebtn, "isDone", true, false);
//戻すを押した持
updateValue(togglebtn, "isDone", false, false);

そして見比べたときに囲った黄色枠からローカルストレージに保存するまでのプログラムをすべて削除します。

これで問題なく動き、コードも短くなりました。
最後に使っていない定数や不要なコードを削除します。

const todoDelete = document.getElementById("js-del-btn"); //削除ボタンのid取得 削除
const todoDone = document.getElementById("js-done-btn"); //完了ボタンのid取得 削除
delbtn.setAttribute("id", "js-del-btn"); //不要なコード削除

以上でリファクタリングは終了です。総コード行数は「142行」(コメント含む)になりました!40行以上短くできました。

最後に

以上でToDoリストは完成です!他にも削除したのを戻せるようにしたり、タスク名を編集したり…など機能をつけることもできますが、今回は一旦ここまでにします。

講師の方にレビューをもらったらそこの修正について追記していきます。
次回からは、このToDoリストをReactで実装する予定です。

最後までお読みいただきありがとうございました。

スポンサーリンク

コメント

タイトルとURLをコピーしました