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);
}
}
});
実際に動くものはここで確認できる。