I am making a to-do list app using basic javascript. I am having a problem with saving the checking status of the checkbox input element and rendering it back when the page is refreshed... I am currently learning javascript, so my whole approach to the checkboxes might need to be changed so tell me about the best method to do so.
here's the code:
let toDoListArray = JSON.parse(localStorage.getItem("items")) || {
toDoListArray: [
{
inputValue: "wash the dishes",
dateValue: "1-1-2023",
check: false,
},
{
inputValue: "checked example 2",
dateValue: "22-3-2025",
check: true,
},
],
};
addItem();
// the code i used for the checkboxes
let list = [];
document.querySelectorAll("input[type=checkbox]").forEach((element) => {
list.push(element);
let idx = list.indexOf(element);
if (toDoListArray[idx].check) {
element.setAttribute("checked", true);
} else if (!toDoListArray[idx].check) {
element.removeAttribute("checked");
}
element.addEventListener("change", () => {
if (element.checked) {
toDoListArray[idx].check = true;
} else {
toDoListArray[idx].check = false;
}
});
});
//end of checkboxes code
function addItem() {
let savedText = document.querySelector(".task-input");
let inputValue = savedText.value;
let savedDate = document.querySelector(".date-input");
let dateValue = savedDate.value;
let check = false;
if (inputValue) {
toDoListArray.push({
inputValue,
dateValue,
check,
});
}
addItemHTML();
savedText.value = "";
savedDate.value = "";
}
function deleteItem(index) {
toDoListArray.splice(index, 1);
addItemHTML();
}
function addItemHTML() {
let addedHTML = "";
for (let i = 0; i < toDoListArray.length; i++) {
let { inputValue, dateValue } = toDoListArray[i];
addedHTML += `
<div class="rendered-list-item">
<input id="check${i}" type="checkbox">
<label for="check${i}">${inputValue}</label>
<div>${dateValue}</div>
<button class="delete" onclick="deleteItem(${i})") >Delete</button>
</div>
`;
}
let jsonString = JSON.stringify(toDoListArray);
localStorage.setItem("items", jsonString);
document.querySelector(".list").innerHTML = addedHTML;
}
* {
margin: 0 auto;
padding: 0;
box-sizing: border-box;
font-family: sans-serif;
}
html {
scroll-behavior: smooth;
}
:root {
--form-hue: 226;
--form-saturation: 53%;
--form-light: 90%;
--form-bg-color: hsl(
var(--form-hue),
var(--form-saturation),
var(--form-light)
);
--header-bg-color: rgba(147, 147, 147, 0.6);
--header-color: aliceblue;
--list-bg-color: rgba(201, 199, 223, 0.3);
--main-bg-color: hsl(221, 70%, 95%);
--add-color: white;
}
body::-webkit-scrollbar {
width: 0.25rem;
}
body::-webkit-scrollbar-track {
background: #c9c9d7;
}
body::-webkit-scrollbar-thumb {
background: rgb(61, 61, 169);
}
main {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
background-color: var(--main-bg-color);
width: 60%;
height: 100%;
padding: 20px;
}
.header,
.form,
.list {
width: 100%;
padding: 10px;
margin: 10px;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.header {
border-radius: 10px;
background-color: var(--header-bg-color);
color: var(--header-color);
font-weight: bold;
}
.form {
background-color: var(--form-bg-color);
border-radius: 10px;
}
.list {
background-color: var(--list-bg-color);
border-radius: 5px;
flex-direction: column;
width: 100%;
}
.task-input,
.date-input,
.add {
border-radius: 10px;
padding: 7px;
margin: 5px;
border: none;
outline: none;
}
.add {
background-color: hsl(
var(--form-hue),
var(--form-saturation),
calc(var(--form-light) * 0.5)
);
color: var(--add-color);
transition: 0.2s;
}
.add:hover {
background-color: hsl(0, 0%, 71%);
scale: 1.07;
font-weight: bold;
cursor: pointer;
}
.add:active {
background-color: aliceblue;
}
.task-input:focus,
.date-input:focus {
background-color: hsl(240, 33%, 95%);
}
.task-input:hover,
.date-input:hover {
outline: 2px solid rgba(62, 93, 152, 0.6);
}
@media only screen and (max-width: 600px) {
main {
width: 100%;
}
}
.rendered-list-item {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
background-color: hsl(0, 100%, 100%);
border-radius: 10px;
padding: 5px;
margin: 5px;
width: 95%;
flex-wrap: wrap;
}
.list .rendered-list-item:nth-child(even) {
background-color: hsla(222, 32%, 88%, 0.824);
}
.list .rendered-list-item:nth-child(even) div:nth-child(3) {
color: hsla(224, 43%, 72%, 0.824);
}
.rendered-list-item:hover {
background-color: hsla(0, 0%, 100%, 0.824);
}
.rendered-list-item label,
div,
button {
padding: 10px;
border-radius: 10px;
border: none;
}
.rendered-list-item button {
align-self: normal;
transition: 0.2s;
margin-right: 5px;
color: hsl(0, 0%, 71%);
}
.rendered-list-item button:hover {
scale: 1.08;
background-color: hsl(0, 65%, 55%);
color: white;
cursor: pointer;
}
.rendered-list-item div:nth-child(3) {
color: hsl(0, 0%, 71%);
}
.rendered-list-item label:nth-child(2) {
background-color: hsl(233, 100%, 98%);
margin-left: 5px;
flex: 1;
transition: 0.5s;
}
.rendered-list-item input[type="checkbox"]:checked + label {
font-weight: bold;
text-decoration: line-through;
color: hsl(0, 0%, 71%);
}
.rendered-list-item input[type="checkbox"] {
align-self: normal;
margin-left: 5px;
opacity: 0.6;
accent-color: hsl(262, 25%, 56%);
width: 0.9rem;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vanilla javascipt ToDoList</title>
</head>
<body>
<main>
<p class="header">To-Do-List</p>
<div class="form">
<input placeholder="type the task" type="text" class="task-input" />
<input type="date" class="date-input" />
<button class="add" onclick="addItem()">Add</button>
</div>
<div class="list"></div>
</main>
</body>
</html>
as u can see, I tried iterating through the checkboxes to add a "checked" attribute to the ones with the index that corresponds with the saved to-do-list array index and also add an event listener to each element to see if they are checked and change their corresponding to-do list object's check property, but when the page is reloaded the array's check property returns back to false and no checked attributes are rendered. when I console log the to-do list array after the event listener and I check the checkbox, the array's check attribute gets updated to true but once I refresh the page, it becomes false again.
There are two issues with your code:
As per your code toDoListArray
should look something like this:
[
{
inputValue: "wash the dishes",
dateValue: "1-1-2023",
check: false,
},
{
inputValue: "checked example 2",
dateValue: "22-3-2025",
check: true,
},
]
But, in the beginning, you are trying to set toDoListArray
as JSON.parse(localStorage.getItem("items"))
which follows the above array format.
And because you using logical OR operator ||
if JSON.parse(localStorage.getItem("items"))
is false
(null
) then we will set the toDoListArray
as
{
toDoListArray: [
{
inputValue: "wash the dishes",
dateValue: "1-1-2023",
check: false,
},
{
inputValue: "checked example 2",
dateValue: "22-3-2025",
check: true,
},
],
}
Now if you see both values of toDoListArray
are diffrent as one is a object and other is an array.
You might have not noticed this as you already have the correct array format save in the localStorage
.
To fix this we can just this:
let toDoListArray = JSON.parse(localStorage.getItem("items")) || [
{
inputValue: "wash the dishes",
dateValue: "1-1-2023",
check: false,
},
{
inputValue: "checked example 2",
dateValue: "22-3-2025",
check: true,
},
];
The second issue is what you have mentioned, that the check and unchecked state of the checkbox is not persistent on reload.
The reason for this is that on change of the checkbox state, you also need to update it in the localStorage
. Like this:
element.addEventListener("change", () => {
if (element.checked) {
toDoListArray[idx].check = true;
} else {
toDoListArray[idx].check = false;
}
localStorage.setItem("items", JSON.stringify(toDoListArray));
});
Final Code:
let toDoListArray = JSON.parse(localStorage.getItem("items")) || [{
inputValue: "wash the dishes",
dateValue: "1-1-2023",
check: false,
},
{
inputValue: "checked example 2",
dateValue: "22-3-2025",
check: true,
},
];
addItem();
// the code i used for the checkboxes
let list = [];
document.querySelectorAll("input[type=checkbox]").forEach((element) => {
list.push(element);
let idx = list.indexOf(element);
if (toDoListArray[idx].check) {
element.setAttribute("checked", true);
} else if (!toDoListArray[idx].check) {
element.removeAttribute("checked");
}
element.addEventListener("change", () => {
if (element.checked) {
toDoListArray[idx].check = true;
} else {
toDoListArray[idx].check = false;
}
localStorage.setItem("items", JSON.stringify(toDoListArray));
});
});
//end of checkboxes code
function addItem() {
let savedText = document.querySelector(".task-input");
let inputValue = savedText.value;
let savedDate = document.querySelector(".date-input");
let dateValue = savedDate.value;
let check = false;
if (inputValue) {
toDoListArray.push({
inputValue,
dateValue,
check,
});
}
addItemHTML();
savedText.value = "";
savedDate.value = "";
}
function deleteItem(index) {
toDoListArray.splice(index, 1);
addItemHTML();
}
function addItemHTML() {
let addedHTML = "";
for (let i = 0; i < toDoListArray.length; i++) {
let {
inputValue,
dateValue
} = toDoListArray[i];
addedHTML += `
<div class="rendered-list-item">
<input id="check${i}" type="checkbox">
<label for="check${i}">${inputValue}</label>
<div>${dateValue}</div>
<button class="delete" onclick="deleteItem(${i})") >Delete</button>
</div>
`;
}
localStorage.setItem("items", JSON.stringify(toDoListArray));
document.querySelector(".list").innerHTML = addedHTML;
}
* {
margin: 0 auto;
padding: 0;
box-sizing: border-box;
font-family: sans-serif;
}
html {
scroll-behavior: smooth;
}
:root {
--form-hue: 226;
--form-saturation: 53%;
--form-light: 90%;
--form-bg-color: hsl( var(--form-hue), var(--form-saturation), var(--form-light));
--header-bg-color: rgba(147, 147, 147, 0.6);
--header-color: aliceblue;
--list-bg-color: rgba(201, 199, 223, 0.3);
--main-bg-color: hsl(221, 70%, 95%);
--add-color: white;
}
body::-webkit-scrollbar {
width: 0.25rem;
}
body::-webkit-scrollbar-track {
background: #c9c9d7;
}
body::-webkit-scrollbar-thumb {
background: rgb(61, 61, 169);
}
main {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
background-color: var(--main-bg-color);
width: 60%;
height: 100%;
padding: 20px;
}
.header,
.form,
.list {
width: 100%;
padding: 10px;
margin: 10px;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.header {
border-radius: 10px;
background-color: var(--header-bg-color);
color: var(--header-color);
font-weight: bold;
}
.form {
background-color: var(--form-bg-color);
border-radius: 10px;
}
.list {
background-color: var(--list-bg-color);
border-radius: 5px;
flex-direction: column;
width: 100%;
}
.task-input,
.date-input,
.add {
border-radius: 10px;
padding: 7px;
margin: 5px;
border: none;
outline: none;
}
.add {
background-color: hsl( var(--form-hue), var(--form-saturation), calc(var(--form-light) * 0.5));
color: var(--add-color);
transition: 0.2s;
}
.add:hover {
background-color: hsl(0, 0%, 71%);
scale: 1.07;
font-weight: bold;
cursor: pointer;
}
.add:active {
background-color: aliceblue;
}
.task-input:focus,
.date-input:focus {
background-color: hsl(240, 33%, 95%);
}
.task-input:hover,
.date-input:hover {
outline: 2px solid rgba(62, 93, 152, 0.6);
}
@media only screen and (max-width: 600px) {
main {
width: 100%;
}
}
.rendered-list-item {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
background-color: hsl(0, 100%, 100%);
border-radius: 10px;
padding: 5px;
margin: 5px;
width: 95%;
flex-wrap: wrap;
}
.list .rendered-list-item:nth-child(even) {
background-color: hsla(222, 32%, 88%, 0.824);
}
.list .rendered-list-item:nth-child(even) div:nth-child(3) {
color: hsla(224, 43%, 72%, 0.824);
}
.rendered-list-item:hover {
background-color: hsla(0, 0%, 100%, 0.824);
}
.rendered-list-item label,
div,
button {
padding: 10px;
border-radius: 10px;
border: none;
}
.rendered-list-item button {
align-self: normal;
transition: 0.2s;
margin-right: 5px;
color: hsl(0, 0%, 71%);
}
.rendered-list-item button:hover {
scale: 1.08;
background-color: hsl(0, 65%, 55%);
color: white;
cursor: pointer;
}
.rendered-list-item div:nth-child(3) {
color: hsl(0, 0%, 71%);
}
.rendered-list-item label:nth-child(2) {
background-color: hsl(233, 100%, 98%);
margin-left: 5px;
flex: 1;
transition: 0.5s;
}
.rendered-list-item input[type="checkbox"]:checked+label {
font-weight: bold;
text-decoration: line-through;
color: hsl(0, 0%, 71%);
}
.rendered-list-item input[type="checkbox"] {
align-self: normal;
margin-left: 5px;
opacity: 0.6;
accent-color: hsl(262, 25%, 56%);
width: 0.9rem;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vanilla javascipt ToDoList</title>
</head>
<body>
<main>
<p class="header">To-Do-List</p>
<div class="form">
<input placeholder="type the task" type="text" class="task-input" />
<input type="date" class="date-input" />
<button class="add" onclick="addItem()">Add</button>
</div>
<div class="list"></div>
</main>
</body>
</html>