Todo list wont refresh after pushing to array - javascript

Whenever I add a todo to my array, it's not being refreshed in html, what do I need to sort this out? Also, how can I connect the Delete button which is being created in loop to a function?
const form = document.querySelector('form')
const input = document.querySelector('input')
const btnAdd = document.querySelector('button')
let ul = document.querySelector('ul')
let todos = ["one", "two", "three"];
pushTodos = (e) => {
e.preventDefault();
todos.push(input.value);
input.value = '';
console.log(todos);
}
for(let i of todos) {
createList = document.createElement("li");
createList.innerHTML+=i + '<button>Delete</delete>';
console.log(todos);
ul.appendChild(createList);
}
form.addEventListener('submit', pushTodos)

The problem you have is that you only update DOM only once on document load. You don't do anything after updating the list. To fix this, create a sync function to synchronize your memory with the DOM and call it every time you update the list:
let todos = ["one", "two", "three"];
const syncTodosWithDOM = () => {
ul.innerHTML = "";
for (let i of todos) {
createList = document.createElement("li");
createList.innerHTML+=i + '<button>Delete</delete>';
console.log(todos);
ul.appendChild(createList);
}
};
pushTodos = (e) => {
e.preventDefault();
todos.push(input.value);
input.value = '';
console.log(todos);
syncTodosWithDOM();
}
syncTodosWithDOM();
form.addEventListener('submit', pushTodos)

Iterate todos inside pushTodos function. Create a separate function createDomElem which will create dom element and call this function again when the element is pushed in the array
pushTodos = (e) => {
e.preventDefault();
todos.push(input.value);
input.value = '';
console.log(todos);
createDomElem()
}
createDomElem = () => {
for (let i of todos) {
createList = document.createElement("li");
createList.innerHTML += i + '<button>Delete</delete>';
console.log(todos);
ul.innerHTML = ''; // remove previous list
ul.appendChild(createList);
}
}
createDomElem() // will create initial list of todos

In pushTodos you also need to refresh HTML.
pushTodos = (e) => {
e.preventDefault();
todos.push(input.value);
input.value = '';
createList = document.createElement("li");
createList.innerHTML+=input.value + '<button>Delete</delete>';
ul.appendChild(createList);
}

Related

How come the last item count is not filtering on delete?

I have a todo list with CRUD. I just implemented a task counter to show the number of tasks left to complete. The count increments/decrements with checking/unchecking items and decrements on deleting items. Except there is one problem. For some reason on the last item deleted, the count stays at 1 instead of going to 0. Full Codepen
Here is what I tried:
function renderTaskCount() {
const incompleteTaskCount = todos.filter((item) => !item.completed).length;
const taskString = incompleteTaskCount === 1 ? 'task' : 'tasks';
listCountElement.innerText = `${incompleteTaskCount} ${taskString} remaining`;
return incompleteTaskCount;
}
const renderTodos = () => {
ul.innerHTML = '';
todos.forEach((item) => {
let li = document.createElement('LI');
li.setAttribute('class', 'item');
li.setAttribute('data-key', item.id);
const itemText = createTodoText(item);
const cb = buildCheckbox(item);
const db = buildDeleteButton(item);
li.append(cb);
li.append(db);
li.append(itemText);
ul.append(li);
//update height of textarea
updateHeight(itemText);
renderTaskCount();
});
};
You have
const renderTodos = () => {
ul.innerHTML = '';
todos.forEach((item) => {
let li = document.createElement('LI');
li.setAttribute('class', 'item');
li.setAttribute('data-key', item.id);
const itemText = createTodoText(item);
const cb = buildCheckbox(item);
const db = buildDeleteButton(item);
li.append(cb);
li.append(db);
li.append(itemText);
ul.append(li);
//update height of textarea
updateHeight(itemText);
renderTaskCount();
});
};
where renderTaskCount updates the count - which is done at the end of each iteration. So if the todos array is empty, the count will never be updated. You need
const renderTodos = () => {
ul.innerHTML = '';
todos.forEach((item) => {
// ...
});
renderTaskCount();
};
so that whether renderTaskCount executes whether or not the todos array is populated.

How to auto grow text on input value?

I have an input form field that outputs text on submit to another created input, essentially an editable todo list. I have tried to make the input text value auto grow, but cannot figure out how to do it. Right now the user has to scroll over to see the rest of the text on each list item. This should not be.
What I tried:
I have tried creating a span and attaching editableContent but that makes my input text disappear.
I have tried setting an attribute on max-length on the created input but cannot get it to work. What is the best way to accomplish auto growing the text input value?
Here is the full codepen
const createTodoText = (todo) => {
const itemText = document.createElement("INPUT");
// const itemText = document.createElement("span");
// itemText.contentEditable
// itemText.contentEditable = 'true'
itemText.classList.add("todoText");
itemText.value = todo.name;
itemText.addEventListener("click", (e) => {
e.currentTarget.classList.add("active");
});
// update todo item when user clicks away
itemText.addEventListener("blur", (e) => {
todo.name = e.currentTarget.value;
renderTodos();
});
return itemText;
};
There you go: -
// select DOM elements
const todoForm = document.querySelector(".todo-form");
const addButton = document.querySelector(".add-button");
const input = document.querySelector(".todo-input");
const ul = document.getElementById("todoList");
let todos = [];
todoForm.addEventListener("submit", function (e) {
e.preventDefault();
addTodo(input.value);
});
const addTodo = (input) => {
if (input !== "") {
const todo = {
id: Date.now(),
name: input,
completed: false
};
todos.push(todo);
renderTodos();
todoForm.reset();
}
};
const renderTodos = (todo) => {
ul.innerHTML = "";
todos.forEach((item) => {
let li = document.createElement("LI");
// li.classList.add('item');
li.setAttribute("class", "item");
li.setAttribute("data-key", item.id);
const itemText = createTodoText(item);
const cb = buildCheckbox(item);
const db = buildDeleteButton(item);
// if (item.completed === true) {
// li.classList.add('checked');
// }
li.append(cb);
li.append(db);
li.append(itemText);
ul.append(li);
});
};
const createTodoText = (todo) => {
const itemText = document.createElement("span");
itemText.setAttribute('role','textbox');
itemText.setAttribute('contenteditable',"true");
itemText.classList.add("todoText");
itemText.innerHTML = todo.name;
itemText.addEventListener("click", (e) => {
e.currentTarget.classList.add("active");
});
// update todo item when user clicks away
itemText.addEventListener("blur", (e) => {
todo.name = e.target.textContent;
renderTodos();
});
return itemText;
};
const buildCheckbox = (todo) => {
const cb = document.createElement('input');
cb.type = 'checkbox';
cb.name = 'checkbox';
cb.classList.add('checkbox');
cb.checked = todo.completed;
// checkbox not staying on current state ??
cb.addEventListener('click', function (e) {
if (e.target.type === 'checkbox') {
// todo.completed = e.target.value;
todo.completed = e.currentTarget.checked
e.target.parentElement.classList.toggle('checked');
}
});
return cb;
};
const buildDeleteButton = (todo) => {
const deleteButton = document.createElement("button");
deleteButton.className = "delete-button";
deleteButton.innerText = "x";
deleteButton.addEventListener("click", function (e) {
// duplicates children sometimes ??
const div = this.parentElement;
div.style.display = "none";
todos = todos.filter((item) => item.id !== todo.id);
});
return deleteButton;
};
// //------ Local Storage ------
function addToLocalStorage(todos) {}
function getFromLocalStorage() {}
// getFromLocalStorage();
This is the Javscript code part. In createTodoText, you can see the changes i've made. It's working according to what you want. What i've done is simple used 'span' instead of 'input'.
How about trying something like
if (todo.name.length) {itemText.size = todo.name.length;}

Local storage in JS not loading items problem

I have a problem with the local storage it seems the items are getting saved to local storage but I cannot make it work to load at start.
Any tips and advice much appreciated.
I am posting the code below.
const input = document.getElementById('input');
const list = document.getElementById('list');
const addButton = document.getElementById('addButton');
const completed = document.getElementById("completed");
let LIST;
let id;
let loadSTORAGE = localStorage.getItem("STORAGE");
if (loadSTORAGE) {
LIST = JSON.parse(loadSTORAGE);
id = LIST.length;
loadList(LIST);
} else {
LIST = [];
id = 0;
}
function loadList() {
LIST.forEach(function() {
addTask();
});
}
addButton.addEventListener("click", addTask);
input.addEventListener("keyup", function(event) {
(event.keyCode === 13 ? addTask() : null)
})
function addTask() {
const newTask = document.createElement("li");
const delBtn = document.createElement("button");
const checkBtn = document.createElement("button");
delBtn.innerHTML = "<button>Reset</button>"
checkBtn.innerHTML = "<button>Done</button>"
if (input.value !== "") {
newTask.textContent = input.value;
list.appendChild(newTask);
newTask.appendChild(checkBtn);
newTask.appendChild(delBtn);
LIST.push({
name: input.value,
id: id,
});
id++
input.value = "";
console.log(LIST);
localStorage.setItem("STORAGE", JSON.stringify(LIST));
}
checkBtn.addEventListener("click", function() {
const parent = this.parentNode
parent.remove();
completed.appendChild(parent);
});
delBtn.addEventListener("click", function() {
const parent = this.parentNode
parent.remove();
});
}
You need to break out the logic of building the item and getting the value. Something like the following where the addTask just makes sure there is input and calls a method that builds an item. Now with the localstorage call, you can call just the code that builds the item.
const input = document.getElementById('input');
const list = document.getElementById('list');
const addButton = document.getElementById('addButton');
const completed = document.getElementById("completed");
const loadSTORAGE = localStorage.getItem("STORAGE");
const LIST = loadSTORAGE ? JSON.parse(loadSTORAGE) : [];
let id = LIST.length;
loadList(LIST);
function loadList() {
LIST.forEach(function(data) {
addTaskElement(data);
});
}
function addTask() {
if (input.value !== "") {
cons newItem = {
name: input.value,
id: id,
};
LIST.push(newItem);
id++;
localStorage.setItem("STORAGE", JSON.stringify(LIST));
input.value = "";
addTaskElement(newItem);
}
}
function addTaskElement(data) {
const newTask = document.createElement("li");
const delBtn = document.createElement("button");
const checkBtn = document.createElement("button");
delBtn.textContent = "Reset"
checkBtn.textContent = "Done"
newTask.textContent = data.name;
newTask.appendChild(checkBtn);
newTask.appendChild(delBtn);
list.appendChild(newTask);
}

Issue with local storage for to-do list

I'm trying to add local storage to my to-do list. While refreshing the page does maintain the list item, the value comes back as undefined. I suspect it's something to do with the lack of an argument when I call the addInput function at the bottom, but I can't see a way around it.
In addition, if the toggled checked class is on and the item is crossed out, is there a way to store the class information?
I'd very much appreciate any help you can give me.
The offending code is below:
https://codepen.io/david-webb/pen/yLeqydK
function saveTodos () {
let jsonstr = JSON.stringify(todos);
localStorage.setItem('todos', jsonstr);
}
function getTodos () {
localStorage.getItem('todoList')
let jsonstr = localStorage.getItem("todos");
todos = JSON.parse(jsonstr);
if (!todos) {
todos = [];
}
}
//cross out text on click
document.addEventListener('click', function(ev) {
if (ev.target.tagName === 'LI') {
ev.target.classList.toggle('checked');
saveTodos ();
}
});
getTodos ();
addInput ();
Try this please:
<input type="text" style="font-size:25px;" id="input" placeholder="Write here">
<button id="addBtn">Add item</button>
<ul id="myUL">
</ul>
<script>
let todo = [];
document.getElementById('addBtn').addEventListener('click', function () {
let value = document.getElementById('input').value;
if (value) {
todo.push(value);
saveTodos()
addInput(value);
}
});
function addInput(text) {
//add list item on click
let listItem = document.createElement('li');
let list = document.getElementById('myUL');
let input = document.getElementById('input').value;
let textNode = document.createTextNode(text);
//create and append remove button
let removeBtn = document.createElement("BUTTON");
list.appendChild(removeBtn);
removeBtn.className = "removeBtn";
removeBtn.innerHTML = "Remove item";
listItem.appendChild(removeBtn);
list.appendChild(listItem);
listItem.appendChild(textNode);
document.getElementById("input").value = "";
removeBtn.addEventListener('click', removeItem);
//console.log(todo);
}
//remove list item on click
function removeItem() {
let item = this.parentNode.parentNode;
let parent = item.parentNode;
let id = parent.id;
let value = parent.innerText;
todo.splice(todo.indexOf(value, 1));
saveTodos();
this.parentNode.parentNode.removeChild(this.parentNode);
console.log(todo)
}
function saveTodos() {
let jsonstr = JSON.stringify(todo);
localStorage.setItem('todos', jsonstr);
}
function getTodos() {
localStorage.getItem('todos')
let jsonstr = localStorage.getItem("todos");
todos = JSON.parse(jsonstr);
if (todos && !todos.length) {
todos = [];
}
else{
if(todos){
for(var intCounter = 0; intCounter < todos.length; intCounter++){
addInput(todos[intCounter]);
}
}
}
}
//cross out text on click
document.addEventListener('click', function (ev) {
if (ev.target.tagName === 'LI') {
ev.target.classList.toggle('checked');
saveTodos();
}
});
getTodos();
// addInput();
</script>
Call addInput within the getTodos function so that as soon as you're done with retreiving the list you print it.
This is what I changed:
function getTodos
function getTodos() {
localStorage.getItem('todos')
let jsonstr = localStorage.getItem("todos");
todos = JSON.parse(jsonstr);
if (todos && !todos.length) {
todos = [];
}
else{
if(todos){
for(var intCounter = 0; intCounter < todos.length; intCounter++){
addInput(todos[intCounter]);
}
}
}
}
Commented addInput().

how to loop through each list and add local stored item in it

I have a Bookmark Page where I add edit and delete bookmarks. and I have stored these items in localStorage. the issue is in loaddata function where I get the stored data and save it back in newly created li. the li tag is storing all the inputs that I typed in just one list. what I want is each bookmark should be within its own list just like additem function. but I don't know how to achieve this
const search = document.querySelector('form input');
const input = document.querySelector('.add-text');
const container = document.querySelector('ul');
let items = null;
let currentItem = null;
let array = [];
const searchItems = function(e) {
if (items) {
let word = e.target.value.toLowerCase();
for (let item of items) {
if (item.firstChild.textContent.toLowerCase().indexOf(word) !== -1) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
}
}
}
const deleteItem = function(e) {
currentItem = null;
e.target.parentNode.remove();
input.value = '';
}
const editItem = function(e) {
currentItem = e.target.parentNode.firstChild;
input.value = currentItem.textContent;
}
const updateItem = function(e) {
if (currentItem) {
currentItem.textContent = input.value;
input.value = '';
}else{
alert('No Selected Text Here to Update');
return;
}
}
const addItem = function() {
let val = input.value;
if (val) {
let li = document.createElement('li');
let inner = '<h1 class="text">' + val + '</h1>';
inner += '<button class="delete">Delete</button>';
inner += '<button class="edit">Edit</button>';
array.push(inner);
let stringified = JSON.stringify(array);
localStorage.setItem('list', stringified);
li.innerHTML = inner;
container.appendChild(li);
input.value = '';
currentItem = li.firstChild;
items = document.querySelectorAll('li');
for (let del of document.querySelectorAll('.delete')) {
del.addEventListener('click', deleteItem);
}
for (let edit of document.querySelectorAll('.edit')) {
edit.addEventListener('click', editItem);
}
} else {
alert('please add some text');
return;
}
}
function loaddata(){
let li = document.createElement('li');
let stringified = localStorage.getItem('list');
let listitems = JSON.parse(stringified);
li.innerHTML = listitems;
container.appendChild(li);
console.log(li);
}
loaddata();
search.addEventListener('keyup', searchItems);
document.querySelector('#add').addEventListener('click', addItem);
document.querySelector('#update').addEventListener('click', updateItem);
Considering your list is an array, you need to loop through it and create adn populate elements within that loop. Try to edit your loaddata function this way:
// Mock content
let container = document.body
localStorage.setItem('list', JSON.stringify(['<h1>Foo</h1>', '<h1>Bar</h1>', '<h1>Baz</h1>']))
loaddata()
// Edited 'loaddata'
function loaddata() {
let stringified = localStorage.getItem('list');
console.log(stringified)
let listitems = JSON.parse(stringified);
for (let i = 0; i < listitems.length; i++) {
let li = document.createElement('li');
li.innerHTML = listitems[i];
container.appendChild(li);
console.log(li);
}
}
It can't be run like a code snippet in Stack Overflow sandbox due to security reasons (accessing Local Storage), so if you want to test it, consider copying to JSFiddle or so.

Categories

Resources