CodeSandboxを使ってToDoアプリを作成中。
前回は、ちょっと挙動がおかしいところを修正して、今回は、ローカルストレージにToDoリストを保存して、画面がリロードされてもリストを保持できるように改良。
過去記事:
ローカルストレージにデータを記録するには、キーと値を対にして記録する方法と、配列やオブジェクトをJSON形式にして記録する方法がある。
Web Storage APIでは?の記事では、キーと値を対にして記録する方法をまとめたので、今回は、オブジェクトの配列をローカルストレージに記録する方法をまとめてみる。
なぜオブジェクトの配列型にするのか?
今回自分の作るToDoアプリは、やることリストに完了・差し戻しボタンがついている。
完了のタスクは、文字をグレーにして取り消し線表示にし、ボタンは完了の代わりに差し戻しボタンに変更する。
つまり、ページリロード時に、やることの一覧だけでなく、各項目の状態(完了・未完了)も保持しておかなければならないからである。
オブジェクトは、item(やることリストの項目)と、comp(完了状態:1、未完了状態:0)のキーを持つものにする。
※完了・未完了のステータスなどがなく、単純にToDoのリストの内容だけ保持するのであれば、ただの配列でOK。
ローカルストレージに保存するデータの作成
JavaScript
// ローカルストレージに保存 let lStorage = []; if (getLocalStorage() !== null) { lStorage = getLocalStorage(); } lStorage.push({ item: task, comp: 0 }); setLocalStorage(lStorage);
入力されたタスクを、未完了状態で、lStorageに追加。
オブジェクトにpushは使えないが、配列の中身がオブジェクトの場合は、pushが使える。
ローカルストレージにオブジェクトの配列を保存
JavaScript
// ローカルストレージにデータを保存 const setLocalStorage = (list) => { localStorage.setItem('todo', JSON.stringify(list)); };
配列やオブジェクトの場合は、JSON形式でローカルストレージに保存されるので、変換してやる必要がある。
todoというキーに対して、JSON形式に変換した配列やオブジェクトを引数に指定して、localStorage.setItemを呼び出す。
ローカルストレージのデータを取り出す
JavaScript
// ローカルストレージのデータを取得 const getLocalStorage = () => { return JSON.parse(localStorage.getItem('todo')); };
今度は逆に、保存したキーを指定して、JSON形式のデータを配列・オブジェクト型に変換して取り出す。
取り出したデータをToDoリストとして整形しなおす
JavaScript
const lStorage = getLocalStorage(); if (lStorage === null) { taskList.textContent = ''; } else { // ローカルストレージに保存してあるデータを戻す for (let i = 0; i < lStorage.length; i++) { const listItem = document.createElement('li'); listItem.id = lStorage[i].item; if (lStorage[i].comp === 1) { listItem.setAttribute('class', 'complete'); } listItem.textContent = lStorage[i].item; taskList.appendChild(listItem); addButtons(listItem); } }
取り出したデータは、ただのオブジェクトの配列になっているので、1つずつデータを取り出して、リストになるように整形していく。
オブジェクトの配列をローカルストレージに保存する例
今回のJavaScript側のコードをまるっと。
const taskInput = document.getElementsByClassName('task_input')[0]; const taskSubmit = document.getElementsByClassName('task_submit')[0]; const taskDelete = document.getElementsByClassName('task_delete')[0]; const taskList = document.getElementsByClassName('task_list')[0]; // ローカルストレージのデータを取得 const getLocalStorage = () => { return JSON.parse(localStorage.getItem('todo')); }; // ローカルストレージにデータを保存 const setLocalStorage = (list) => { localStorage.setItem('todo', JSON.stringify(list)); }; // Deleteボタン押下時の処理 const deleteTasks = (deleteButton) => { const chosenTask = deleteButton.closest('li'); taskList.removeChild(chosenTask); // ローカルストレージに保存 const lStorage = getLocalStorage(); for (let i = 0; i < lStorage.length; i++) { if (lStorage[i].item === chosenTask.id) { lStorage.splice(i, 1); setLocalStorage(lStorage); break; } } }; // Completeボタン押下時の処理(完了⇔未完了のトグル) const completeTasks = (completeButton) => { const chosenTask = completeButton.closest('li'); if (chosenTask.className === 'complete') { // 完了タスクを未完了に戻す chosenTask.className = 'notComplete'; completeButton.innerHTML = 'Complete'; } else { // 未完了タスクを完了にする chosenTask.className = 'complete'; completeButton.innerHTML = 'Restore'; } // 完了・未完了のデータをローカルストレージに保存 const lStorage = getLocalStorage(); for (let i = 0; i < lStorage.length; i++) { if (lStorage[i].item === chosenTask.id) { if (chosenTask.className === 'complete') { lStorage[i].comp = 1; } else { lStorage[i].comp = 0; } setLocalStorage(lStorage); break; } } }; // やること追加ボタン押下時③ ⇒ ボタンの作成 const addButtons = (item) => { const deleteButton = document.createElement('button'); deleteButton.className = 'task_button'; deleteButton.innerHTML = 'Delete'; item.appendChild(deleteButton); const completeButton = document.createElement('button'); completeButton.className = 'task_button'; item.appendChild(completeButton); if (item.className === 'complete') { completeButton.innerHTML = 'Restore'; } else { completeButton.innerHTML = 'Complete'; } deleteButton.addEventListener('click', (evt) => { evt.preventDefault(); deleteTasks(deleteButton); }); completeButton.addEventListener('click', (evt) => { evt.preventDefault(); completeTasks(completeButton); }); }; // やること追加ボタン押下時② ⇒ タスクの要素を追加 const addTasks = (task) => { const listItem = document.createElement('li'); const showItem = taskList.appendChild(listItem); listItem.id = task; const listItem2 = document.createElement('p'); const showItem2 = showItem.appendChild(listItem2); showItem2.innerHTML = task; addButtons(listItem); // ローカルストレージに保存 let lStorage = []; if (getLocalStorage() !== null) { lStorage = getLocalStorage(); } lStorage.push({ item: task, comp: 0 }); setLocalStorage(lStorage); }; // やること追加ボタン押下時① ⇒ エラー処理 taskSubmit.addEventListener('click', (evt) => { evt.preventDefault(); const task = taskInput.value; if (task === '') { // タスク未入力は認めない window.alert('タスクを入力してください'); return; } const lStorage = getLocalStorage(); if (lStorage !== null) { for (let i = 0; i < lStorage.length; i++) { if (lStorage[i].item === task) { // タスクの重複は認めない window.alert('タスクが重複しています'); return; } } } addTasks(task); taskInput.value = ''; }); // 全削除ボタン押下時 ⇒ リストの全削除(LoaclStorageも全削除) taskDelete.addEventListener('click', (evt) => { evt.preventDefault(); localStorage.clear(); taskList.textContent = ''; }); // Reload時の処理 document.addEventListener('DOMContentLoaded', () => { const lStorage = getLocalStorage(); if (lStorage === null) { taskList.textContent = ''; } else { // ローカルストレージに保存してあるデータを戻す for (let i = 0; i < lStorage.length; i++) { const listItem = document.createElement('li'); listItem.id = lStorage[i].item; if (lStorage[i].comp === 1) { listItem.setAttribute('class', 'complete'); } listItem.textContent = lStorage[i].item; taskList.appendChild(listItem); addButtons(listItem); } } });
実際に動くものはここで確認できる。