sp_menu

【JavaScript】ToDoリストの作り方を解説

【JavaScript】ToDoリストの作り方を解説

今回はJavaScriptでToDoリストの作り方をご紹介します。

この記事では以下のようなお悩みを解決できます。

  • JavaScriptでToDOリストの作り方を知りたい
       

1. ToDoリストの完成イメージ

まずはTodoリストの完成イメージを見てみましょう。

フォームにタスクと期日を入力して追加ボタンを押すと下にタスクが表示されます(期日の入力は任意)。タスクを入力せずに追加ボタンを押すとエラーメッセージを表示するようにします。

タスクを追加するとタスク内容、期日、完了ボタン、削除ボタンが表示されます。完了ボタンはタスクを完了したときに押すボタンで完了したタスクをわかりやすくするために色を変えます。

「資料作成」のタスクが黄色くなっていますがこれが完了している状態です。削除ボタンを押すとタスクが削除されます。

2. ToDoリストの概要

2-1. ToDoリストに必要な表示・機能

先程のToDoリストの完成イメージに必要な機能は以下の7つになります。

ToDoリストのタスク入力部分

①タスクの入力フォーム

タスクを入力するためのフォームです。フォームに入力しないまま追加ボタンを押すとフォームの下にエラーメッセージを表示するようにします。

②期日の入力フォーム

期日を入力するフォームです。期日は入力しなくても追加できるようにします。

③追加ボタン

タスクを追加するボタンです。

ToDoリストのタスク表示部分

④タスクの表示

入力したタスクを表示します。

⑤期日を表示

②で期日を入力している場合は表示します。

⑥完了ボタン

タスクが完了したら押すボタンで、押したらタスクの背景色を黄色にする。

⑦削除ボタン

タスクを削除するボタンです。

2-2. 追加したデータの保存方法

追加したタスクのデータをどこかに保存しなければ、リロードしたりブラウザを閉じると消えてしまいます。今回は追加したタスクをローカルストレージに保存するようにします。

ローカルストレージはブラウザにデータを保存する仕組みのことです。ローカルストレージについてよくわかならない方は以下の記事で解説していますのでよろしければ参考にしてください!

ローカルストレージはブラウザにデータを保存しますので「Chrome」でタスクを追加した場合、「Chrome」にデータが保存されますので「Safari」や「Microsoft Edge」など他のブラウザでToDoリストを開いても追加したタスクのデータはありませんのでその点は注意してください。

       

3. ToDoリストの実装

ToDoリストはHTML、CSS、JavaScriptで実装します。コードは以下のようになります。

   <div class="wrapper">
    <h1>TODOリスト</h1>
    <div class="input-area">
      <div class="task-area">
        <label for="task-input">タスク(必須)</label>
        <!--タスクの入力フォーム-->
        <input type="text" class="task-input" id="task-input">
    <!--エラーメッセージ-->
        <div class="error-msg">タスクを入力してください!</div>
      </div>
      <div class="date-area">
        <label for="date">期日</label>
        <!--期日の入力フォーム-->
        <input type="date" class="date" id="date">
      </div>
    <!--追加ボタン-->
      <button class="btn add-btn">追加</button>
    </div>
    <div class="display-task">
      <!--タスクを表示する-->
      <ul class="task-list"></ul>
    </div>
  </div>
const inputForm = document.querySelector('.task-input');
const inputDate = document.querySelector('.date');
const addBtn = document.querySelector('.add-btn');
const taskList = document.querySelector('.task-list');

//ページがロードされたら処理を実行する
window.addEventListener('load', () => {
 displayTasks();
 TaskListBtnEvent();
});

//追加ボタンを押したら処理を実行する
addBtn.addEventListener('click', () => {
 //タスクの入力フォームが空の場合
 if(!inputForm.value) {
  const errorMsg = document.querySelector('.error-msg');
  //errorMsgにshowクラスを追加する
  errorMsg.classList.add('show');
  return;
 }
 //タスクidを設定する
 let taskId = setTaskId();
 //タスクのデータのオブジェクトを作成する
 const task = {
  id: taskId,
  content: inputForm.value,
  date: inputDate.value ? formattedDate(inputDate.value) : null,
 }
 //タスクリスト(ulタグ)にタスクを追加する 
 taskList.innerHTML += createTaskElement(task); 
 //完了ボタン、削除ボタンのイベント
 TaskListBtnEvent();
 //ローカルストレージにタスクデータを保存する
 saveLocalStorage(task);
 //入力フォームをリセットする
 inputForm.value = '';
 inputDate.value = '';
});

//タスクの入力フォームでキーが離されたときに処理を実行する
inputForm.addEventListener('keyup', () => {
  const errorMsg = document.querySelector('.error-msg');
 //errorMsgにshowクラスがある場合
  if(errorMsg.classList.contains('show')) {
  //タスクの入力フォームが空がどうか
    if(inputForm.value !== '') {
   //errorMsgのshowクラスを取り除く
      errorMsg.classList.remove('show');
    }
  }
});

//タスクを表示するためのHTMLタグを作成する
const createTaskElement = (task) => {
    return `<li class="task-item" data-task-id="${task.id}">
    ${task.content}
    <div class="item-wrapper">
     ${task.date ? `<div class="item-date">期日:${task.date}</div>`:''} 
     <div class="item-btn">
      <button class="btn complete-btn">完了</button>
      <button class="btn delete-btn" data-task-id="${task.id}">削除</button>
     </div>
    </div>
   </li>`;
}
 
//ローカルストレージにタスクを保存する
const saveLocalStorage = (task) => { 
 //ローカルストレージに保存されているタスクデータを取得する
  const tasks = JSON.parse(localStorage.getItem('tasks')) || [];
 //tasksに入力したタスクデータを追加する
  tasks.push(task);
 //ローカルストレージにtasksを保存する
  localStorage.setItem('tasks', JSON.stringify(tasks));
}

//ローカルストレージにタスクがある場合は表示する
const displayTasks = () => {
 //taskuList(ulタグ)をリセットする
 taskList.innerHTML = '';
 //ローカルストレージに保存されているタスクデータを取得する
  const tasks = JSON.parse(localStorage.getItem('tasks')) || [];
  //ローカルストレージにタスクのデータが1つ以上ある場合
  if(tasks.length !== 0) {
  //タスクを1つずつ取り出して処理をする
    tasks.forEach((task) => {
   //taskList(ulタグ)にタスクを追加する
      taskList.innerHTML += createTaskElement(task);
    });
  }
}

//タスクのidをセットする
const setTaskId = () => {
 //ローカルストレージに保存されているタスクデータを取得する
 const tasks = JSON.parse(localStorage.getItem('tasks')) || [];
 //ローカルストレージにタスクのデータが1つ以上ある場合
 if(tasks.length !== 0) {
  const task = tasks[tasks.length - 1];
  return task.id + 1;
 }
 return 1;
}

//期日のフォーマットを変更する
const formattedDate = (dateString) => {
 const selectedDate = new Date(dateString);
 const year = selectedDate.getFullYear();
 const month = selectedDate.getMonth() + 1; 
 const day = selectedDate.getDate();
 const getDay = selectedDate.getDay();
 const daysOfWeek = ['日','月','火','水','木','金','土'];
 const dayOfWeek = daysOfWeek[getDay];
    
 return `期日:${year}年${month}月${day}日(${dayOfWeek})`;  
}

//タスクの完了や削除の処理を実装
const TaskListBtnEvent = () => {
 const deleteBtns = document.querySelectorAll('.delete-btn');
 const compBtns = document.querySelectorAll('.complete-btn');
 //deleteBtnsを1つずつ取り出して処理を実行する
 deleteBtns.forEach((deleteBtn) => {
 //削除ボタンをクリックすると処理を実行する
  deleteBtn.addEventListener('click', (e) => {
  //削除するタスクのliタグを取得
  const deleteTarget = e.target.closest('.task-item');
  //ローカルストレージに保存されているタスクデータを取得する
  const tasks = JSON.parse(localStorage.getItem('tasks')) || [];
  //削除するタスクのliタグのデータ属性(タスクid)を取得
  const targetId = deleteTarget.closest('li').dataset.taskId;
  //tasksから削除するタスクを取り除く
  const updatedTasks = tasks.filter(task => task.id !== parseInt(targetId));
  //ローカルストレージにupdatedTasksを保存する
  localStorage.setItem('tasks', JSON.stringify(updatedTasks));
  //taskListから削除するタスクを取り除く
  taskList.removeChild(deleteTarget.closest('li'));
  });
 });
  
//compBtnsを1つずつ取り出して処理を実行する
compBtns.forEach((compBtn) => {
 //完了ボタンをクリックすると処理を実行する
  compBtn.addEventListener('click', (e) => {
    //完了するタスクのliタグを取得
      const compTarget = e.target.closest('li');
      //compTargetにcompleteクラスがない場合は追加、ある場合は削除する
      compTarget.classList.toggle('complete');
   });
 });
}
*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

li {
  list-style: none;
}

.btn {
  color: #FFF;
  font-weight: bold;
  border: none;
  border-radius: 3px;
  width: 50px;
  height: 50px;
  cursor: pointer;
  margin-left: 10px;
}

body {
  background: #f1f1f1;
}

.wrapper {
  width: 100%;
  text-align: center;
  margin: 0 auto;
  padding: 100px 20px 0;
  max-width: 1000px;
}

h1 {
  color: #333;
  margin-bottom: 60px;
}

.input-area {
  text-align: left;
  margin: 0 auto;
  width: 100%;
  display: flex;
  justify-content: space-between;
}

input {
  border: 1px solid #CCC;
  outline: none;
  border-radius: 5px;
}

.task-area {
  width: 70%;
}

.date-area {
  width: 15%;
}

label {
  font-size: 14px;
  font-weight: bold;
}

.task-input {
  font-size: 18px;
  padding: 10px;
  margin-right: 10px;
  width: 100%;
}

.error-msg {
  font-size: 12px;
  font-weight: bold;
  color: red;
  display: none;
}

.error-msg.show {
  display: block;
}

.date {
  border: 1px solid #CCC;
  padding: 12px 10px;
}

.add-btn {
  background: #3ca83c;
  margin-top: 20px;
}

.display-task {
  text-align: left;
  width: 100%;
  margin: 100px auto 0 auto;
}

.task-item {
  font-size: 20px;
  font-weight: bold;
  color: #333;
  margin-bottom: 20px;
  border-radius: 5px;
  border: 1px solid #CCC;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #FFF;
  padding: 8px 20px;
  transition: 0.3s linear;
}

.item-wrapper {
  display: flex;
  justify-content: flex-end;
}

.item-date {
  background: #000000;
  color: #FFF;
  padding: 5px 10px;
  border-radius: 20px;
  font-size: 12px;
  display: flex;
  align-items: center;
  margin-right: 20px;
}

.complete {
  background: #ffef64;
}

.delete-btn {
  background: #e95656;
}

.complete-btn {
  background: #0881f1;
}

3-1. HTMLの解説

 <div class="wrapper">
    <h1>TODOリスト</h1>
    <div class="input-area">
      <div class="task-area">
        <label for="task-input">タスク(必須)</label>
        <!--タスクの入力フォーム-->
        <input type="text" class="task-input" id="task-input">
    <!--エラーメッセージ-->
        <div class="error-msg">タスクを入力してください!</div>
      </div>
      <div class="date-area">
        <label for="date">期日</label>
        <!--期日の入力フォーム-->
        <input type="date" class="date" id="date">
      </div>
    <!--追加ボタン-->
      <button class="btn add-btn">追加</button>
    </div>
    <div class="display-task">
      <!--タスクを表示する-->
      <ul class="task-list"></ul>
    </div>
  </div>

7行目:<input type=”text” class=”task-input” id=”task-input”>

タスクを入力するためのフォームです。

9行目: <div class=”error-msg”>タスクを入力してください!</div>

タスクの入力フォームが空のまま追加ボタンを押したときに出るエラーメッセージです。

14行目:<input type=”date” class=”date”>

期日を入力するためのフォームです。

17行目:<button class=”btn add-btn”>追加</button>

タスクを追加するボタンです。

21行目:<ul class=”task-list”></ul>

タスクをリスト形式で表示します。タスクを表示するliタグなどの子要素はJavaScriptで生成するのでulタグの中は空のままにします。

       

3-2. JavaScriptの解説

const inputForm = document.querySelector('.task-input');
const inputDate = document.querySelector('.date');
const addBtn = document.querySelector('.add-btn');
const taskList = document.querySelector('.task-list');

1行目:const inputForm = document.querySelector(‘.task-input’);

document.querySelector(‘クラス名’)は指定したクラス名の要素を取得しています。ここではタスクの入力フォームを取得しています。

//ページがロードされたら処理を実行する
window.addEventListener('load', () => {
 displayTasks();
 TaskListBtnEvent();
});

7-10行目:window.addEventListener(‘load’, () => {~});

window.addEventListener(‘load’, () => {~})はページが読み込まれたらdisplayTasks()、TaskListBtnEvent()を実行します。

displayTasks()はローカルストレージにタスクが保存されている場合はブラウザに表示します。TaskListBtnEvent()は完了、削除ボタンのイベントを定義した関数です。TaskListBtnEvent()は119-151行目の解説を参照してください。

//追加ボタンを押したら処理を実行する
addBtn.addEventListener('click', () => {
 //タスクの入力フォームが空の場合
 if(!inputForm.value) {
  const errorMsg = document.querySelector('.error-msg');
  //errorMsgにshowクラスを追加する
  errorMsg.classList.add('show');
  return;
 }
 //タスクidを設定する
 let taskId = setTaskId();
 //タスクのデータのオブジェクトを作成する
 const task = {
  id: taskId,
  content: inputForm.value,
  date: inputDate.value ? formattedDate(inputDate.value) : null,
 }
 //タスクリスト(ulタグ)にタスクを追加する 
 taskList.innerHTML += createTaskElement(task); 
 //ローカルストレージにタスクデータを保存する
 saveLocalStorage(task);
 //完了ボタン、削除ボタンのイベント
 TaskListBtnEvent();
 //入力フォームをリセットする
 inputForm.value = '';
 inputDate.value = '';
});

13-38行目:addBtn.addEventListener(‘click’, () => {~});

追加ボタンをクリックすると処理を実行します。

15-20行目:if(!inputForm.value) {~};

タスクの入力フォームが空の場合はerrorMsgにshowクラスを追加してエラーメッセージを表示し、タスクの追加処理を終了します。

18行目:errorMsg.classList.add(‘show’);

要素.classList.add(‘クラス名’)は要素に指定したクラス名を追加します。ここではerrorMsgにshowクラスを追加しています。

22行目:let taskId = setTaskId();

タスクのidを取得します。setTaskId()については94-103行目の解説を参照してください。

24-28行目:const task = {~};

タスクのオブジェクトを作成します。idtaskIdcontentタスクの入力内容date期日がある場合は期日なければnullが設定されます。formattedDate()については106-116行目の解説を参照してください。

30行目:taskList.innerHTML += createTaskElement(task);

taskList(ulタグ)にタスクを追加しています。要素.innerHTMLは要素内のHTMLの取得や設定をします。createTaskElement()はタスク、期日を表示するためのliタグを返す関数です。createTaskElement()については54-65行目の解説を参照してください。

32行目:TaskListBtnEvent();

完了、削除ボタンのイベントを定義した関数を呼び出して、追加したタスクのボタンも動作するようにします。TaskListBtnEvent()については118-150行目の解説を参照してください。

34行目:saveLocalStorage(task);

saveLocalStorage()は引数で指定したタスクのデータをローカルストレージに保存する関数です。saveLocalStorage()については68-75行目の解説を参照してください。

36行目:inputForm.value = ”;

37行目:inputDate.value = ”;

追加ボタンを押した後に入力したタスクや期日の入力フォームをリセットします。

//タスクの入力フォームでキーが離されたときに処理を実行する
inputForm.addEventListener('keyup', () => {
  const errorMsg = document.querySelector('.error-msg');
 //errorMsgにshowクラスがある場合
  if(errorMsg.classList.contains('show')) {
  //タスクの入力フォームが空じゃないとき処理を実行
   if(inputForm.value !== '') {
   //errorMsgのshowクラスを取り除く
      errorMsg.classList.remove('show');
    }
  }
});

41-51行目:inputForm.addEventListener(‘keyup’, () => {~});

inputForm.addEventListener(‘keyup’, () => {~});の「keyup」はキーが離されたときに発生するイベントでタスクの入力フォームに文字を入力してキーから指を離すと処理を実行します。

処理内容はerrorMsgにshowクラスがある場合、showクラスを取り除きます。タスクの入力フォームにエラーメッセージが表示されているときに文字を入力するとエラーメッセージを非表示にします。

44-50行目:if(errorMsg.classList.contains(‘show’)) {~}

要素.classList.contains(‘クラス名’)は要素に指定したクラスがある場合は「true」、ない場合は「false」を返します。ここではerrorMsgにshowクラスがある場合は処理を実行します。

48行目:errorMsg.classList.remove(‘show’);

要素.classList.remove(‘クラス名’)は要素の指定したクラスを取り除きます。ここではerrorMsgからshowクラスを取り除きます。

//タスクを表示するためのHTMLタグを作成する
const createTaskElement = (task) => {
    return `<li class="task-item" data-task-id="${task.id}">
    ${task.content}
    <div class="item-wrapper">
     ${task.date ? `<div class="item-date">期日:${task.date}</div>`:''} 
     <div class="item-btn">
      <button class="btn complete-btn">完了</button>
      <button class="btn delete-btn" data-task-id="${task.id}">削除</button>
     </div>
    </div>
   </li>`;
}

54-65行目:const createTaskElement = (task) => {~}

createTaskElement()はタスクを表示するためのHTMLタグや、期日、完了、削除ボタンをretuenで返します。HTMLタグにはテンプレートリテラル(${~})があるのでバッククォート(` `)で囲むことに注意して下さい。

55行目:<li class=”task-item” data-task-id=”${task.id}”>

data-task-id=”${task.id}”はliタグにデータ属性にタスクのidを設定します。このデータ属性はタスクの削除するときの特定に使用されます。

56行目:${task.content}

${task.content}はタスクの内容を表示します。

58行目:${task.date ? `<div class=”item-date”>期日:${task.date}</div>` : ‘ ‘}

${task.date ? `<div class=”item-date”>期日:${task.date}</div>` : ‘ ‘} は三項演算子で期日が入力されているかどうかで条件分岐しています。

入力されている場合は<div class=”item-date”>期日:${task.date}</div>を表示し、入力されていない場合は空表示にします。

<div class=”item-date”>期日:${task.date}</div>にテンプレートリテラル(${~})があるのでバッククォート(` `)で囲むことに注意して下さい。

61行目: <button class=”btn delete-btn” data-task-id=”${task.id}”>削除</button>

削除ボタンにもデータ属性にタスクidを設定します。このデータ属性も削除する時に使用されます。

//ローカルストレージにタスクを保存する
const saveLocalStorage = (task) => { 
 //ローカルストレージに保存されているタスクデータを取得する
  const tasks = JSON.parse(localStorage.getItem('tasks')) || [];
 //tasksに入力したタスクデータを追加する
  tasks.push(task);
 //ローカルストレージにtasksを保存する
  localStorage.setItem('tasks', JSON.stringify(tasks));
}

68-75行目:const saveLocalStorage = (task) => {~};

saveLocalStorage()はタスクのデータをローカルストレージに保存する関数です。

70行目:const tasks = JSON.parse(localStorage.getItem(‘tasks’)) || [];

tasksはローカルストレージにタスクのデータがある場合は取得し、ない場合は空の配列になります。localStorage.getItem()はキーを指定してローカルストレージに保存してあるデータを取得します。

ローカルストレージはデータを保存するときにキーと値をセットで保存するため、取得するときはキーに対応する値(データ全体)取得します。ローカルストレージから取得したデータはJSON形式になっているのでJSON.parse()で配列のデータに変換しています。

72行目:tasks.push(task);

push()は配列の末尾に要素を追加します。ここではtasksに入力したタスクデータを追加します。

74行目:localStorage.setItem(‘tasks’, JSON.stringify(tasks));

localStorage.setItem()はローカルストレージにデータを保存します。ローカルローカルストレージはキーをセットで保存をします。第1引数のtasksはキーで第2引数のJSON.stringify(tasks)は値になります。

JSON.stringify()は指定したデータをJSON形式に変換します。ローカルストレージに配列のデータを保存するにはJSON形式に変換する必要があります。ローカルストレージについてわからない方は以下の記事で説明していますのでよろしければ参考にしてください。

//ローカルストレージにタスクがある場合は表示する
const displayTasks = () => {
 //taskuList(ulタグ)をリセットする
 taskList.innerHTML = '';
 //ローカルストレージに保存されているタスクデータを取得する
  const tasks = JSON.parse(localStorage.getItem('tasks')) || [];
  //ローカルストレージにタスクのデータが1つ以上ある場合
  if(tasks.length !== 0) {
  //タスクを1つずつ取り出して処理をする
    tasks.forEach((task) => {
   //taskList(ulタグ)にタスクを追加する
      taskList.innerHTML += createTaskElement(task);
    });
  }
}

78-89行目:const displayTasks = () => {~}

displayTasks()はローカルストレージにタスクのデータがある場合は取得してブラウザに表示する関数です。

//タスクのidをセットする
const setTaskId = () => {
 //ローカルストレージに保存されているタスクデータを取得する
 const tasks = JSON.parse(localStorage.getItem('tasks')) || [];
 //ローカルストレージにタスクのデータが1つ以上ある場合
 if(tasks.length !== 0) {
  const task = tasks[tasks.length - 1];
  return task.id + 1;
 }
 return 1;
}

94-103行目:const setTaskId = () => {~}

setTaskId()はタスクのidを設定します。ローカルストレージにタスクのデータがある場合は一番最後に追加したタスクのidに1足したものを返し、ない場合は1を返します。

99行目:const task = tasks[tasks.length – 1];

tasks.lengthは配列の要素数を取得します。「taks.length – 1」は配列の最後の要素のインデックスを取得しています。配列のインデックスは0から始まるので一番最後の要素を取得するには配列の要素数から1を引く必要があります。

100行目:return task.id + 1;

配列の最後の要素のタスクidに1を足してものを返しています。これが次に追加されるタスクのidになります。

//期日のフォーマットを変更する
const formattedDate = (dateString) => {
 const selectedDate = new Date(dateString);
 const year = selectedDate.getFullYear();
 const month = selectedDate.getMonth() + 1; 
 const day = selectedDate.getDate();
 const getDay = selectedDate.getDay();
 const daysOfWeek = ['日','月','火','水','木','金','土'];
 const dayOfWeek = daysOfWeek[getDay];
    
 return `期日:${year}年${month}月${day}日(${dayOfWeek})`;  
}

106-116行目:const formattedDate = (dateString) => {~}

<input type=”date”>で入力した日付は通常2024年1月1日の場合「2024-01-01」と表示されます。formattedDate()は「期日:2024年1月1日(月)」と表示するようにフォーマットを変更します。

<input type=”date”>のフォーマットの変更については詳しい解説は以下の記事で紹介していますのでよろしければ参考にしてください。

//タスクの完了や削除の処理を実装
const TaskListBtnEvent = () => {
 const deleteBtns = document.querySelectorAll('.delete-btn');
 const compBtns = document.querySelectorAll('.complete-btn');
 //deleteBtnsを1つずつ取り出して処理を実行する
 deleteBtns.forEach((deleteBtn) => {
 //削除ボタンをクリックすると処理を実行する
  deleteBtn.addEventListener('click', (e) => {
  //削除するタスクのliタグを取得
  const deleteTarget = e.target.closest('.task-item');
  //ローカルストレージに保存されているタスクデータを取得する
  const tasks = JSON.parse(localStorage.getItem('tasks')) || [];
  //削除するタスクのliタグのデータ属性(タスクid)を取得
  const targetId = deleteTarget.closest('li').dataset.taskId;
  //tasksから削除するタスクを取り除く
  const updatedTasks = tasks.filter(task => task.id !== parseInt(targetId));
  //ローカルストレージにupdatedTasksを保存する
  localStorage.setItem('tasks', JSON.stringify(updatedTasks));
  //taskListから削除するタスクを取り除く
  taskList.removeChild(deleteTarget.closest('li'));
  });
 });
  
//compBtnsを1つずつ取り出して処理を実行する
compBtns.forEach((compBtn) => {
 //完了ボタンをクリックすると処理を実行する
  compBtn.addEventListener('click', (e) => {
    //完了するタスクのliタグを取得
      const compTarget = e.target.closest('li');
      //compTargetにcompleteクラスがない場合は追加、ある場合は削除する
      compTarget.classList.toggle('complete');
   });
 });
}

119-151行目:const TaskListBtnEvent = () => {~}

TaskListBtnEvent()はタスクの完了、削除ボタンのイベントを定義した関数です。

120行目:const deleteBtns = document.querySelectorAll(‘.delete-btn’);

document.querySelectorAll(‘クラス名’)は指定したクラスの要素を全て取得しています。ここではすべてのタスクの削除ボタンを取得しています。

123-139行目:deleteBtns.forEach((deleteBtn) => {~});

deleteBtnsの要素(それぞれのタスクの削除ボタン)をforEachで1つずつ取り出して処理を実行します。

125-138行目:deleteBtn.addEventListener(‘click’, (e) => {~});

削除ボタンを押すと処理を実行します。

127行目:const deleteTarget = e.target.closest(‘.task-item’);

e.targetはイベントを発生した要素を取得します。これはクリックした削除ボタンを指します。closest(‘クラス名’)は要素の祖先要素のクラスを取得します。

deleteTargetは削除ボタンを押したタスクのliタグ(祖先要素)を取得しています。

129行目:const tasks = JSON.parse(localStorage.getItem(‘tasks’)) || [];

tasksはローカルストレージに保存されているデータがある場合は取得し、ない場合は空の配列になります

131行目:const targetId = deleteTarget.closest(‘li’).dataset.taskId;

削除ボタンを押したタスクのliタグのデータ属性(タスクid)を取得します。

133行目:const updatedTasks = tasks.filter(task => task.id !== parseInt(targetId));

filter()は配列の要素を1つずつ取り出して、指定した条件を満たす要素で新しい配列を生成します。

ここでは削除ボタンのデータ属性のタスクidとliタグのデータ属性のタスクidが一致しないデータ、つまり削除するタスクを除き、それ以外のタスクで新しい配列データを生成しています。

parseInt()は文字列を数値に変換します。データ属性は文字列になっているのでtask.idと比較するために数値に変換する必要があります。

135行目:localStorage.setItem(‘tasks’, JSON.stringify(updatedTasks));

updatedTasksをローカルストレージに保存します。localStorage.setItem()に関しては74行目の解説を参照してください。

137行目:taskList.removeChild( deleteTarget.closest(‘li’) );

taskList(ulタグ)から削除対象のタスクを取り除きます。これを記述することでブラウザの画面からタスクを削除します。

142-150行目:compBtn.addEventListener(‘click’, (e) => {~});

完了ボタンをクリックすると処理を実行します。

148行目:compTarget.classList.toggle(‘complete’);

toggle()は要素に指定したクラスがない場合はクラスを追加、ある場合は削除します。ここでは完了ボタンをクリックしたタスクのliタグにcompleteクラスの追加または削除します。

3-3. CSSの解説

.error-msg {
  font-size: 12px;
  font-weight: bold;
  color: red;
  display: none;
}

.error-msg.show {
  display: block;
}

80-82行目:.error-msg.show { display: block; }

タスクの入力フォームが空欄のままで追加ボタンを押すとerror-msgクラスにshowクラスが追加されエラーメッセージが表示されます。

4. まとめ

今回はJavaScriptでToDoリストを作る方法をご紹介しました。JavaScriptのコード量が多くなり解説も分かりづらい部分があったかもしれません。

初めてToDoリストを作る方は最初はローカルストレージを使わないでタスクだけを表示させるだけのシンプルなToDoリストを作り後からいろいろな機能やデータ保存の設定を追加していくといいかもしれません。ぜひご自身でも作成してみてください!

関連記事