I am almost done with my to-do app, what is left is to do the local storage for the completed list and edited task.
The local storage I have done for when the task is added and removed. But I am not sure how to do the local storage for when the task is set to complete and when the task has been edited.
HTML
<div class="form">
<input class="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
JS
//local storage key
const STORAGE_KEY = "tasks-storage-key";
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time"),
};
const updateEl = {
formUpdate: document.querySelector(".form-update"),
inputUpdate: document.querySelector(".user-input"),
modal: document.querySelector(".modal"),
dateUpdate: document.querySelector(".date-update"),
timeUpdate: document.querySelector(".time-update"),
};
//Create ID
const createId = () =>
`${Math.floor(Math.random() * 10000)}${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? "[]");
renderList();
function makeNewTask() {
const data = {
id: createId(),
taskNew: el.input.value,
taskDate: el.date.value,
taskTime: el.time.value,
};
return data;
}
function updateTask() {
const dataUpdate = {
id: createId(),
inputUpdate: updateEl.inputUpdate.value,
dateUpdate: updateEl.dateUpdate.value,
timeUpdate: updateEl.timeUpdate.value,
};
return dataUpdate;
}
function renderList() {
// This resets the list innerHTML to the new list
el.list.innerHTML = taskList.map(function (data) {
return `<div class="task">
<div class="task-content">
<div class="task" data-id="${data.id}">
<input class="new-task-created" value="${data.taskNew}" readonly></input>
<input class="due-date" type="date" value="${data.taskDate}" readonly></input>
<input class="due-time" type="time" value="${data.taskTime}" readonly></input>
</div>
<div class="action-buttons">
<button onclick="editItem(event)" class="edit" data-id="${data.id}">Edit</button>
<button onclick="deleteItem(event)" class="delete" data-id="${data.id}">Delete</button>
<button onclick="completeItem(event)" class="complete" data-id="${data.id}">Complete</button>
</div>`;
})
el.input.value = "";
}
//event listner that listens for add button.
function addTask() {
taskList.push(makeNewTask());
// store the list on localstorage because data changed
storeList();
// render list again because you've added a new entry
renderList();
}
//function that removes task from array with delete button.
function deleteItem(event) {
taskList.splice(taskList.indexOf(event.target.dataset.id), 1);
// store the list on localstorage because data changed
storeList();
// render list again because entry was removed
renderList();
}
//function that stores task list.
function storeList() {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(taskList));
}
//function that that edits tasks with date and time.
function editItem(event) {
const editEl = event.target.closest(".task");
let taskUpdate = editEl.querySelector(".new-task-created");
let dateUpdate = editEl.querySelector(".due-date");
let timeUpdate = editEl.querySelector(".due-time");
let editbtn = editEl.querySelector(".edit");
if (editbtn.innerHTML.toLowerCase() == "edit"){
taskUpdate.removeAttribute("readonly");
dateUpdate.removeAttribute("readonly");
timeUpdate.removeAttribute("readonly");
taskUpdate.focus();
editbtn.innerHTML = "Save";
}
else{
taskUpdate.setAttribute("readonly", "readonly");
dateUpdate.setAttribute("readonly", "readonly");
timeUpdate.setAttribute("readonly", "readonly");
editbtn.innerHTML = "Edit";
}
}
//function that that completes task.
function completeItem(event) {
const element = event.target.closest(".task-content");
let taskItem = element.querySelector(".new-task-created");
let dateItem = element.querySelector(".due-date");
let timeItem = element.querySelector(".due-time");
// style..
taskItem.style.textDecoration = "line-through";
dateItem.style.textDecoration = "line-through";
timeItem.style.textDecoration = "line-through";
}
I have added a screenshot of the section of the code I am focusing on, I just put the entire code so you can see the flow.
here in some changes in your js file look into it.
in editItem function on behalf of i we change value in taskList.
in completeItem function on behalf of i we add one more prop in object textDecoration: true and in renderList function we use prop to add style.
pass index i to each button action in function like editItem, deleteItem, completeItem .
//local storage key
const STORAGE_KEY = "tasks-storage-key";
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time"),
};
const updateEl = {
formUpdate: document.querySelector(".form-update"),
inputUpdate: document.querySelector(".user-input"),
modal: document.querySelector(".modal"),
dateUpdate: document.querySelector(".date-update"),
timeUpdate: document.querySelector(".time-update"),
};
//Create ID
const createId = () =>
`${Math.floor(Math.random() * 10000)}${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? "[]");
renderList();
function makeNewTask() {
const data = {
id: createId(),
taskNew: el.input.value,
taskDate: el.date.value,
taskTime: el.time.value,
};
return data;
}
function updateTask() {
const dataUpdate = {
id: createId(),
inputUpdate: updateEl.inputUpdate.value,
dateUpdate: updateEl.dateUpdate.value,
timeUpdate: updateEl.timeUpdate.value,
};
return dataUpdate;
}
function renderList() {
// This resets the list innerHTML to the new list
// <input class="new-task-created" value="${data.taskNew}" readonly style="text-decoration: 'line-through'"></input>
el.list.innerHTML = taskList.map(function (data, i) {
return `<div class="task">
<div class="task-content">
<div class="task" data-id="${data.id}" >
<input class="new-task-created" value="${data.taskNew}" readonly style="${data.textDecoration ? 'text-decoration: line-through': ''}"></input>
<input class="due-date" type="date" value="${data.taskDate}" readonly></input>
<input class="due-time" type="time" value="${data.taskTime}" readonly></input>
</div>
<div class="action-buttons">
<button onclick="editItem(event, ${i})" class="edit" data-id="${data.id}">Edit</button>
<button onclick="deleteItem(event, ${i})" class="delete" data-id="${data.id}">Delete</button>
<button onclick="completeItem(event, ${i})" class="complete" data-id="${data.id}">Complete</button>
</div>`;
})
el.input.value = "";
}
//event listner that listens for add button.
function addTask() {
taskList.push(makeNewTask());
// store the list on localstorage because data changed
storeList();
// render list again because you've added a new entry
renderList();
}
//function that removes task from array with delete button.
function deleteItem(event, i) {
taskList.splice(i, 1);
// store the list on localstorage because data changed
storeList();
// render list again because entry was removed
renderList();
}
//function that stores task list.
function storeList() {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(taskList));
}
//function that that edits tasks with date and time.
function editItem(event, i) {
const editEl = event.target.closest(".task");
let taskUpdate = editEl.querySelector(".new-task-created");
let dateUpdate = editEl.querySelector(".due-date");
let timeUpdate = editEl.querySelector(".due-time");
let editbtn = editEl.querySelector(".edit");
if (editbtn.innerHTML.toLowerCase() == "edit") {
taskUpdate.removeAttribute("readonly");
dateUpdate.removeAttribute("readonly");
timeUpdate.removeAttribute("readonly");
taskUpdate.focus();
editbtn.innerHTML = "Save";
}
else {
taskUpdate.setAttribute("readonly", "readonly");
dateUpdate.setAttribute("readonly", "readonly");
timeUpdate.setAttribute("readonly", "readonly");
editbtn.innerHTML = "Edit";
taskList[i] = {
id: taskList[i].id,
taskNew: taskUpdate.value,
taskDate: dateUpdate.value,
taskTime: timeUpdate.value
}
// store the list on localstorage because data changed
storeList();
// render list again because you've added a new entry
renderList();
}
}
//function that that completes task.
function completeItem(event, i) {
const element = event.target.closest(".task-content");
let taskItem = element.querySelector(".new-task-created");
let dateItem = element.querySelector(".due-date");
let timeItem = element.querySelector(".due-time");
// style..
taskItem.style.textDecoration = "line-through";
dateItem.style.textDecoration = "line-through";
timeItem.style.textDecoration = "line-through";
taskList[i] = {
...taskList[i],
textDecoration: true
}
console.log('taskList', taskList)
// store the list on localstorage because data changed
storeList();
// render list again because you've added a new entry
renderList();
}
For edited tasks I would first modify the updateTask() function so that it uses the same id as the originally created task:
//...
const taskData = {
id: createId(),
completed: false, // added
contents: el.input.value,
date: el.date.value,
time: el.time.value,
};
//...
You can modify the contents and the date/time to keep track of the last time it was modified. From there, you would do the simply call storeList() and renderList() to save to local storage and update the view.
For completed tasks I would either add a boolean property in the task data (something like completed) or a completion date (initially set to zero or null). From there you simply update and check for that value. And as for saving to local storage you would do the same: call storeList() and renderList().
Related
I created an editItem function which creates the HTML element when the user clicks on the edit button, I am not sure how to go about when the user inputs, when the update replaces the existing task.
I am aware that the replace can be used to replace the new-task-created class in the renderList
Just a note I created a new object updateEl which contains the class of the modal and I also created updateTask function which returns the new values been inserted.
My thinking pattern is this: If I need to create new values for the user input, time, and date, that new information can just replace the old information when they click save (UpdateTask function)
HTML
<div class="form">
<input class ="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
<div class="modal"></div>
JS
//local storage key
const STORAGE_KEY = "tasks-storage-key";
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time"),
};
const updateEl = {
formUpdate: document.querySelector(".form-update"),
inputUpdate: document.querySelector(".user-input"),
modal: document.querySelector(".modal"),
dateUpdate: document.querySelector(".date-update"),
timeUpdate: document.querySelector(".time-update"),
};
//Create ID
const createId = () =>
`${Math.floor(Math.random() * 10000)}${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? "[]");
renderList();
function makeNewTask() {
const data = {
id: createId(),
taskNew: el.input.value,
taskDate: el.date.value,
taskTime: el.time.value,
};
return data;
}
function updateTask() {
const dataUpdate = {
id: createId(),
inputUpdate: updateEl.inputUpdate.value,
dateUpdate: updateEl.dateUpdate.value,
timeUpdate: updateEl.timeUpdate.value,
};
return dataUpdate;
}
function renderList() {
// This resets the list innerHTML to the new list
el.list.innerHTML = taskList.map(function (data) {
return `<div class="task">
<div class="task-content">
<div class="task" data-id="${data.id}">
<div class="new-task-created">${data.taskNew}</div>
<label class="due-date">${data.taskDate}</label>
<label class="due-time">${data.taskTime}</label>
</div>
<div class="action-buttons">
<button onclick="editItem(event)" class="edit" data-id="${data.id}">Edit</button>
<button onclick="deleteItem(event)" class="delete" data-id="${data.id}">Delete</button>
<button onclick="completeItem(event)" class="complete" data-id="${data.id}">Complete</button>
</div>
</div>`;
});
}
//event listner that listens for add button.
function addTask() {
taskList.push(makeNewTask());
// store the list on localstorage because data changed
storeList();
// render list again because you've added a new entry
renderList();
}
//function that removes task from array with delete button.
function deleteItem(event) {
taskList.splice(taskList.indexOf(event.target.dataset.id), 1);
// store the list on localstorage because data changed
storeList();
// render list again because entry was removed
renderList();
}
//function that stores task list.
function storeList() {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(taskList));
}
//function that that edits tasks with date and time.
function editItem() {
let updateInput = document.createElement("div")
updateInput.innerHTML = `<div class="form-update">
<input class ="input-update" type="text">
<input class="date-update" type="date">
<input class="time-update" type="time">
<button onclick="UpdateTask()" class="update" id="update">Save</button>
<button onclick="close()" class="close" id="close">Save</button>
</div>`;
updateEl.modal.appendChild(updateInput);
}
function UpdateTask(){
}
//function that that completes task.
function completeItem(event) {
const element = event.target.closest(".task-content");
let taskItem = element.querySelector(".new-task-created");
let dateItem = element.querySelector(".due-date");
let timeItem = element.querySelector(".due-time");
// style..
taskItem.style.textDecoration = "line-through";
dateItem.style.textDecoration = "line-through";
timeItem.style.textDecoration = "line-through";
}
I got it to update doing it this way.
function editItem(event) {
const editEl = event.target.closest(".task");
let taskUpdate = editEl.querySelector(".new-task-created");
let dateUpdate = editEl.querySelector(".due-date");
let timeUpdate = editEl.querySelector(".due-time");
let editbtn = editEl.querySelector(".edit");
if (editbtn.innerHTML.toLowerCase() == "edit"){
taskUpdate.removeAttribute("readonly");
dateUpdate.removeAttribute("readonly");
timeUpdate.removeAttribute("readonly");
taskUpdate.focus();
editbtn.innerHTML = "Save";
}
else{
taskUpdate.setAttribute("readonly", "readonly");
dateUpdate.setAttribute("readonly", "readonly");
timeUpdate.setAttribute("readonly", "readonly");
editbtn.innerHTML = "Edit";
}
I have created a to-do app, but for some reason, the list is stored in local storage, but when I refresh the browser the list refuses to stay on the browser after the refresh.
I have not done any CSS just yet I only have the HTML and JS so far
HTML
<div class="form">
<input class ="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
JS
//local storage key
const STORAGE_KEY = "tasks-storage-key";
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time"),
};
//Create ID
const createId = () =>
`${Math.floor(Math.random() * 10000)}${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? "[]");
function makeNewTask() {
const data = {
id: createId(),
taskNew: el.input.value,
taskDate: el.date.value,
taskTime: el.time.value,
};
return data;
}
//function that creates new tasks with date and time
function display() {
const tasks = document.createElement("div");
data = makeNewTask();
let newtask = tasks.innerHTML = `
<div class="task-content">
<div class="task" data-id="${data.id}">
<div class="new-task-created">${data.taskNew}</div>
<label class="due-date">${data.taskDate}</label>
<label class="due-time">${data.taskTime}</label>
</div>
<div class="action-buttons">
<button onclick="editItem(event)" class="edit" data-id="${data.id}">Edit</button>
<button onclick="deleteItem(event)" class="delete" data-id="${data.id}">Delete</button>
<button onclick="completeItem(event)" class="complete" data-id="${data.id}">Complete</button>
</div>
</div>`;
taskList.push(data);
console.log(taskList);
el.list.appendChild(tasks);
storeList();
}
//event listner that listens for add button.
function addTask() {
display();
}
//function that stores task list.
function storeList() {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(taskList));
}
//function that removes task from array with delete button.
function deleteItem() {
let removeitem = document.querySelector(".task-content");
removeitem.parentNode.removeChild(removeitem);
window.localStorage.removeItem(STORAGE_KEY);
}
//function that removes stored task when deleted.
//function that that edits tasks with date and time.
function editItem() {}
//function that that completes task.
function completeItem(event) {
const element = event.target.closest(".task-content");
console.log(element);
let taskItem = element.querySelector(".new-task-created");
let dateItem = element.querySelector(".due-date");
let timeItem = element.querySelector(".due-time");
// style..
taskItem.style.textDecoration = "line-through";
dateItem.style.textDecoration = "line-through";
timeItem.style.textDecoration = "line-through";
}
I am not sure how to make it stay on the browser after a refresh. I even added window.localStorage hoping that would do the trick.
You localStorage is not empty, according to your second screenshot, problem is that you simply not rendering the data that's already in localStorage
renderList(taskList) // renders existing tasks from localStorage
function display() {
data = makeNewTask();
taskList.push(data); // add new task to list
renderList(taskList); // render list items
storeList();
}
// render all items
function renderList (list) {
list.forEach(function (data) {
const tasks = document.createElement("div");
let newtask = tasks.innerHTML = `
<div class="task-content">
<div class="task" data-id="${data.id}">
<div class="new-task-created">${data.taskNew}</div>
<label class="due-date">${data.taskDate}</label>
<label class="due-time">${data.taskTime}</label>
</div>
<div class="action-buttons">
<button onclick="editItem(event)" class="edit" data-id="${data.id}">Edit</button>
<button onclick="deleteItem(event)" class="delete" data-id="${data.id}">Delete</button>
<button onclick="completeItem(event)" class="complete" data-id="${data.id}">Complete</button>
</div>
</div>`;
el.list.appendChild(tasks);
})
}
The function addTask() is the function that when the user clicks the "+" button it adds the list to the array. I have a render function that renders the to-do list when the "+" is added. I sat and thought about how I could do this.
doing the taskList.push()``` in the ```function addTask()``` might work, but the issue I am having is I am not sure how to push the elements of the ```function renderList(list)``` into the taskList.push()```.
I am missing something in this approach, but I am not sure what. I know I need to access the ```let newtask```` and add that bit to the array.
HTML
<div class="form">
<input class ="user-input" type="text">
<input class="date" type="date">
<input class="time" type="time">
<button onclick="addTask()" class="add" id="add">+</button>
</div>
<div class="list"></div>
JS
//local storage key
const STORAGE_KEY = "tasks-storage-key";
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time"),
};
//Create ID
const createId = () =>
`${Math.floor(Math.random() * 10000)}${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? "[]");
function makeNewTask() {
const data = {
id: createId(),
taskNew: el.input.value,
taskDate: el.date.value,
taskTime: el.time.value,
};
return data;
}
//function that creates new tasks with date and time
function display() {
data = makeNewTask();
taskList.push(data);
renderList(taskList);
storeList();
}
function renderList(list) {
list.forEach(function (data) {
const tasks = document.createElement("div");
let newtask = (tasks.innerHTML = `
<div class="task-content">
<div class="task" data-id="${data.id}">
<div class="new-task-created">${data.taskNew}</div>
<label class="due-date">${data.taskDate}</label>
<label class="due-time">${data.taskTime}</label>
</div>
<div class="action-buttons">
<button onclick="editItem(event)" class="edit" data-id="${data.id}">Edit</button>
<button onclick="deleteItem(event)" class="delete" data-id="${data.id}">Delete</button>
<button onclick="completeItem(event)" class="complete" data-id="${data.id}">Complete</button>
</div>
</div>`);
el.list.appendChild(tasks);
});
}
//event listner that listens for add button.
function addTask() {
taskList.push(list);
}
//function that stores task list.
function storeList() {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(taskList));
}
//function that removes task from array with delete button.
function deleteItem() {
let removeitem = document.querySelector(".task-content");
removeitem.parentNode.removeChild(removeitem);
window.localStorage.removeItem(STORAGE_KEY);
}
//function that removes stored task when deleted.
//function that that edits tasks with date and time.
function editItem() {}
//function that that completes task.
function completeItem(event) {
const element = event.target.closest(".task-content");
console.log(element);
let taskItem = element.querySelector(".new-task-created");
let dateItem = element.querySelector(".due-date");
let timeItem = element.querySelector(".due-time");
// style..
taskItem.style.textDecoration = "line-through";
dateItem.style.textDecoration = "line-through";
timeItem.style.textDecoration = "line-through";
}
This is the error I get.
There are a few issues with your code, but let's start with the error:
Well the issue is that addTask function does not receive a list parameter that you are trying to push. I think you want to call makeNewTask() there instead of passing that list that is undefined.
//event listner that listens for add button.
function addTask() {
taskList.push(makeNewTask());
}
The next problem is that display() is never called. Also, why are you creating an empty task when calling display? The display function is redundant, you should only call renderList(taskList). You should call storeList() only when it changes (creation of new task or removal of one).
Next problem is in your renderList() function. You are assigning that newTask variable that you never actually use. Also if you're appending children to the main element in a forEach on that list, it will keep adding new lists of elements. I don't think you want that.
function renderList() {
el.list.innerHTML = taskList.map(function (data) {
return `<div class="task">
<div class="task-content">
<div class="task" data-id="${data.id}">
<div class="new-task-created">${data.taskNew}</div>
<label class="due-date">${data.taskDate}</label>
<label class="due-time">${data.taskTime}</label>
</div>
<div class="action-buttons">
<button onclick="editItem(event)" class="edit" data-id="${data.id}">Edit</button>
<button onclick="deleteItem(event)" class="delete" data-id="${data.id}">Delete</button>
<button onclick="completeItem(event)" class="complete" data-id="${data.id}">Complete</button>
</div>
</div>`;
});
}
Also, renderList should use the taskList instead of receiving a list parameter since you're already storing the taskList globally (which is not great either).
The final working code should be something like this (added some comments as well -- I suggest using classes for structuring your data but I'll leave that up to you):
//local storage key
const STORAGE_KEY = "tasks-storage-key";
// variables object
const el = {
form: document.querySelector(".form"),
input: document.querySelector(".user-input"),
list: document.querySelector(".list"),
date: document.querySelector(".date"),
time: document.querySelector(".time"),
};
//Create ID
const createId = () => `${Math.floor(Math.random() * 10000)}${new Date().getTime()}`;
//variable of empty array that gets new task
let taskList = JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? "[]");
// This should be called initially after you read the data from localstorage in order to display the initial data if you have any.
renderList();
function makeNewTask() {
const data = {
id: createId(),
taskNew: el.input.value,
taskDate: el.date.value,
taskTime: el.time.value,
};
return data;
}
function renderList() {
// actually reset the list innerHTML to the new list (in order to facilitate removing / adding -- not very efficient)
el.list.innerHTML = taskList.map(function (data) {
return `<div class="task">
<div class="task-content">
<div class="task" data-id="${data.id}">
<div class="new-task-created">${data.taskNew}</div>
<label class="due-date">${data.taskDate}</label>
<label class="due-time">${data.taskTime}</label>
</div>
<div class="action-buttons">
<button onclick="editItem(event)" class="edit" data-id="${data.id}">Edit</button>
<button onclick="deleteItem(event)" class="delete" data-id="${data.id}">Delete</button>
<button onclick="completeItem(event)" class="complete" data-id="${data.id}">Complete</button>
</div>
</div>`;
});
}
//event listner that listens for add button.
function addTask() {
taskList.push(makeNewTask());
// store the list on localstorage because data changed
storeList();
// render list again because you've added a new entry
renderList();
}
//function that removes task from array with delete button.
function deleteItem(event) {
taskList.splice(taskList.indexOf(event.target.dataset.id), 1);
// store the list on localstorage because data changed
storeList();
// render list again because you've removed an entry
renderList();
}
//function that stores task list.
function storeList() {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(taskList));
}
//function that that edits tasks with date and time.
function editItem() { }
//function that that completes task.
function completeItem(event) {
const element = event.target.closest(".task-content");
let taskItem = element.querySelector(".new-task-created");
let dateItem = element.querySelector(".due-date");
let timeItem = element.querySelector(".due-time");
// style..
taskItem.style.textDecoration = "line-through";
dateItem.style.textDecoration = "line-through";
timeItem.style.textDecoration = "line-through";
}
I'd like to add that since completeItem function only changes the current DOM, it cannot be stored on localStorage so on refresh you won't know which tasks are completed. I would suggest adding a property completed to each taskList entry so you can recreate the completed state when refreshing.
as it was mentioned by Cristian the error that you are getting is because the function is not receiving the parameter ‘list’, you can simply try to put this instead:
function addTask() {
display();
}
Then you should see the task on the UI but there are more things that you will need to improve related with your code logic
I am making a simple to do app and am stuck on updating the data array when a new task is added. Right now, if you add a new task, the entire array re-populates on the display and duplicate values are listed. How do I resolve this so that only one iteration of the array displays any time it is updated on the page?
const completedDivs = document.getElementById("completed");
const taskDivs = document.getElementById("tasks");
const addBtn = document.querySelector(".fa-plus");
const input = document.getElementById("newTask");
// SET UP DEFAULT TO DO TASKS
let todos = [
{ completed: false, task: "Make Coffee", id: 1 },
{ completed: false, task: "Walk the dog", id: 2 },
{ completed: true, task: "Make calculator app", id: 3 },
];
// SET UP REQUIRED VARIABLES
let taskNo = (completedDivsLength + taskDivsLength) + 1;
// MAP OUT DEFAULT TASKS TO DISPLAY
function generateToDo() {
todos.map(todo => {
// CREATE A NEW DIV
let newTask = document.createElement("div");
// GIVE THE DIV AN ID
newTask.id = `div${taskNo}`;
if(todo.completed) {
// FORMAT THE DIV
newTask.innerHTML = `<input type="checkbox" id="task${taskNo}" checked>
<label for="task${taskNo}">${todo.task}</label>
<i class="far fa-trash-alt" id="trash${taskNo}"></i>`
// Check the item off the list
newTask.classList.add("checked");
// Add the task to the completed list
completedDivs.appendChild(newTask);
} else if (!todo.completed) {
// FORMAT THE DIV
newTask.innerHTML = `<input type="checkbox" id="task${taskNo}">
<label for="task${taskNo}">${todo.task}</label>
<i class="far fa-trash-alt" id="trash${taskNo}"></i>`
// Uncheck the task from the list
newTask.classList.remove("checked");
// Move the task to the To Do list
taskDivs.appendChild(newTask);
}
// INCREMENT THE TASK NO BY 1
taskNo++;
});
}
// ADD NEW TASKS
addBtn.addEventListener("click", () => {
todos.push({ completed: false, task: `${input.value}`, id: taskNo });
generateToDo();
// RESET THE INPUT FIELD TO NOTHING
input.value = "";
});
// AUTOMATICALLY LOAD THE DEFAULT TO DOS
window.onload = generateToDo();
You should not iterate on the whole array each time a task is added.
Just create an element for the new task.
Only in on load iterate over the array.
// MAP OUT DEFAULT TASKS TO DISPLAY
function generateToDo(todo) {
// CREATE A NEW DIV
let newTask = document.createElement("div");
// GIVE THE DIV AN ID
newTask.id = `div${taskNo}`;
if(todo.completed) {
// FORMAT THE DIV
newTask.innerHTML = `<input type="checkbox" id="task${taskNo}" checked>
<label for="task${taskNo}">${todo.task}</label>
<i class="far fa-trash-alt" id="trash${taskNo}"></i>`
// Check the item off the list
newTask.classList.add("checked");
// Add the task to the completed list
completedDivs.appendChild(newTask);
} else if (!todo.completed) {
// FORMAT THE DIV
newTask.innerHTML = `<input type="checkbox" id="task${taskNo}">
<label for="task${taskNo}">${todo.task}</label>
<i class="far fa-trash-alt" id="trash${taskNo}"></i>`
// Uncheck the task from the list
newTask.classList.remove("checked");
// Move the task to the To Do list
taskDivs.appendChild(newTask);
}
// INCREMENT THE TASK NO BY 1
taskNo++;
}
// ADD NEW TASKS
addBtn.addEventListener("click", () => {
const newTodo = { completed: false, task: `${input.value}`, id: taskNo };
todos.push(newTodo);
generateToDo(newTodo);
// RESET THE INPUT FIELD TO NOTHING
input.value = "";
});
// AUTOMATICALLY LOAD THE DEFAULT TO DOS
window.onload = todos.forEach( todo => generateToDo(todo));
maybe you need these couple of lines - delete what is already there and then update
// MAP OUT DEFAULT TASKS TO DISPLAY
function generateToDo() {
[].forEach.call(completedDivs.children,(x)=>x.remove());
[].forEach.call(taskDivs.children,(x)=>x.remove());
todos.map(todo => {
// CREATE A NEW DIV
let newTask = document.createElement("div");
...
I am doing a task list with an editable function for the each task item. What I expect is that when I update item's value, the value in LocalStorage update simultaneously. Currently, the value in LocalStorage can be updated, however, it only updates the last value of it no matter which item's value I modify. And the one should be changed does not be modified. How do I change correct localStorage value when I revise the task item?
const todo__input = document.querySelector(".todo__input")
const add__btn = document.querySelector(".add__btn")
const item__sector = document.querySelector(".item__sector")
function createToDoItem(toDoItem) {
const position = "beforeend"
const item = `
<div class="item">
<input type="checkbox" class="done__btn">
<input type="text" class="item__content" value="${toDoItem}" disabled>
<button class="edit__btn"><i class="far fa-edit"></i></button>
<button class="delete__btn"><i class="far fa-trash-alt"></i></button>
</div>
`
item__sector.insertAdjacentHTML(position, item)
return item__sector
}
// load todo item from localstorage when page is loaded
document.addEventListener("DOMContentLoaded", getLocalStorage)
// add item to the item sector
add__btn.addEventListener("click", e => {
e.preventDefault()
const input__value = todo__input.value
if (input__value.trim() === "") { return }
createToDoItem(input__value)
saveLocalStorage(input__value)
todo__input.value = ""
})
// keypress Enter
document.addEventListener("keypress", e => {
if (e.keyCode == 13) {
e.preventDefault()
const input__value = todo__input.value
if (input__value.trim() === "") { return }
createToDoItem(input__value)
saveLocalStorage(input__value)
todo__input.value = ""
}
})
// the function on item (done, edit, and delete)
item__sector.addEventListener("click", e => {
const parent = e.target.parentElement
// done
if (e.target.classList.contains("done__btn")) {
e.target.nextElementSibling.classList.toggle("done__color")
}
// edit the todo item
if (e.target.classList.contains("edit__btn")) {
if (e.target.previousElementSibling.disabled.disabled == true) {
e.target.previousElementSibling.disabled = !e.target.previousElementSibling.disabled
} else {
e.target.previousElementSibling.disabled = !e.target.previousElementSibling.disabled
e.target.previousElementSibling.setAttribute("value", e.target.previousElementSibling.value)
editLocalStorage(e.target.previousElementSibling)
}
}
// delete todo item
if (e.target.classList.contains("delete__btn")) {
parent.remove()
deleteLocalStorage(e.target.previousElementSibling.previousElementSibling)
}
})
// function for check todo status in the LocalStorage
function checkLocalStorage() {
let todos
if (localStorage.getItem("todos") === null) {
todos = []
} else {
todos = JSON.parse(localStorage.getItem("todos"))
}
return todos
}
// function for save localstorage
function saveLocalStorage(todo) {
const todos = checkLocalStorage()
todos.push(todo)
localStorage.setItem("todos", JSON.stringify(todos))
}
// function for get item and render to the screen from localstorage
function getLocalStorage() {
const todos = checkLocalStorage()
todos.forEach(todo => {
createToDoItem(todo)
})
}
// edit localStorage
function editLocalStorage(todo) {
const todos = checkLocalStorage()
const todoIndex = todo.getAttribute("value")
todos.splice(todos.indexOf(todoIndex), 1, todoIndex)
localStorage.setItem("todos", JSON.stringify(todos))
}
====
<body>
<div class="container">
<h1 class="title">My To-Do List</h1>
<form class="add__todo">
<input type="text" class="todo__input" placeholder="Add a task...">
<button class="add__btn">Add</button>
</form>
<div class="item__sector">
</div>
<div class="item__status">
<button class="all">All</button>
<button class="completed">COMPLETE</button>
<button class="incompleted">UNCOMPLETE</button>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js"></script>
<script src="app.js"></script>
</body>
The reason that causes your solution to edit the last value is:-
The todoIndex variable inside the editLocalStorage function is referencing the new updated value from your input which is not yet stored inside the todos array in local storage therefore indexOf() returns -1 hence editing the last value.
I have rewritten the JS with a tweak to the functions item__sector.addEventListener, editLocalStorage and added a global variable edit__index
Code Snippet
const todo__input = document.querySelector(".todo__input")
const add__btn = document.querySelector(".add__btn")
const item__sector = document.querySelector(".item__sector")
let edit__index = -1
function createToDoItem(toDoItem) {
const position = "beforeend"
const item = `
<div class="item">
<input type="checkbox" class="done__btn">
<input type="text" class="item__content" value="${toDoItem}" disabled>
<button class="edit__btn"><i class="far fa-edit"></i></button>
<button class="delete__btn"><i class="far fa-trash-alt"></i></button>
</div>
`
item__sector.insertAdjacentHTML(position, item)
return item__sector
}
// load todo item from localstorage when page is loaded
document.addEventListener("DOMContentLoaded", getLocalStorage)
// add item to the item sector
add__btn.addEventListener("click", e => {
e.preventDefault()
const input__value = todo__input.value
if (input__value.trim() === "") { return }
createToDoItem(input__value)
saveLocalStorage(input__value)
todo__input.value = ""
})
// keypress Enter
document.addEventListener("keypress", e => {
if (e.keyCode == 13) {
e.preventDefault()
const input__value = todo__input.value
if (input__value.trim() === "") { return }
createToDoItem(input__value)
saveLocalStorage(input__value)
todo__input.value = ""
}
})
// the function on item (done, edit, and delete)
item__sector.addEventListener("click", e => {
const parent = e.target.parentElement
// done
if (e.target.classList.contains("done__btn")) {
e.target.nextElementSibling.classList.toggle("done__color")
}
// edit the todo item s
if (e.target.classList.contains("edit__btn")) {
if (e.target.previousElementSibling.disabled.disabled == true) {
e.target.previousElementSibling.disabled = !e.target.previousElementSibling.disabled
} else {
const todos = checkLocalStorage()
if (edit__index === -1) {
const valueBeforeEdit = e.target.previousElementSibling.getAttribute("value")
edit__index = todos.indexOf(valueBeforeEdit)
} else {
const valueAfterEdit = e.target.previousElementSibling.value
editLocalStorage(edit__index, valueAfterEdit)
edit__index = -1
}
e.target.previousElementSibling.disabled = !e.target.previousElementSibling.disabled
e.target.previousElementSibling.setAttribute("value", e.target.previousElementSibling.value)
}
}
// delete todo item
if (e.target.classList.contains("delete__btn")) {
parent.remove()
deleteLocalStorage(e.target.previousElementSibling.previousElementSibling)
}
})
// function for check todo status in the LocalStorage
function checkLocalStorage() {
let todos
if (localStorage.getItem("todos") === null) {
todos = []
} else {
todos = JSON.parse(localStorage.getItem("todos"))
}
return todos
}
// function for save localstorage
function saveLocalStorage(todo) {
const todos = checkLocalStorage()
todos.push(todo)
localStorage.setItem("todos", JSON.stringify(todos))
}
// function for get item and render to the screen from localstorage
function getLocalStorage() {
const todos = checkLocalStorage()
todos.forEach(todo => {
createToDoItem(todo)
})
}
// edit localStorage
function editLocalStorage(editIndex, editValue) {
const todos = checkLocalStorage()
todos.splice(editIndex, 1, editValue)
localStorage.setItem("todos", JSON.stringify(todos))
debugger
}
Note:
There is an edge case of having more than one todo item with the same value that you need to solve for.