Issue with local storage for to-do list - javascript

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().

Related

Why doesn't my js code clear the input field

I'm trying to clear my input field in my simplified to-do list but I can't get my code to clear the input when I add a new task.
If you look in the JS code you can see a simple code (input.value = "";) to clear the input but it doesn't work. Also the IF function that is supposed to have an alert message if you don't put any text in the input field doesn't work for some reason either.
I don't really understand how I can get them to work, I've been looking on W3 but can't find any good answers.
<h2>To-do list!</h2>
<div>
<input type="text" id="task" placeholder="Write task" />
<button id="my-button">Add task</button>
</div>
<ul id="list"></ul>
var btn = document.getElementById('my-button').onclick = function() {
var ul = document.getElementById("list");
var li = document.createElement("li");
var input = document.getElementById('task').value;
if (input != '') {
addItem();
}
function createDeleteButton() {
var deleteButton = document.createElement('button');
deleteButton.classList.add("deleteButton");
deleteButton.textContent = "x";
deleteButton.addEventListener("click", removeItem);
return deleteButton;
}
function addItem() {
let listElement = document.createElement('li');
listElement.innerText = input;
listElement.appendChild(createDeleteButton());
if (input.value != "") {
list.appendChild(listElement)
} else
alert("Please write something");
input.value = "";
}
function removeItem() {
let parent = this.parentNode.parentNode;
let child = this.parentNode;
parent.removeChild(child);
}
}
There are some problems with your code.
You are wrapping all the functions inside add button onClick function. No need to do that, it make things complicated.
You are first checking if input is empty, so when addItem function executes, the inner if else will never be executed:
} else alert("Please write something");
How to do it?
1 Declare the elements you want to use:
const ul = document.getElementById("list");
const input = document.getElementById("task");
const addBtn = document.getElementById("my-button");
2 Make your add button's onClick function to do only what it have to do: add an item.
addBtn.onclick = addItem;
3 Declare the addItem function:
function addItem() {
if (input.value !== "") {
const listElement = document.createElement("li");
listElement.innerText = input.value;
listElement.appendChild(createDeleteButton());
ul.appendChild(listElement);
input.value = "";
} else {
alert("Please write something");
}
}
The addItem function first checks if input value is empty, and if it is not empty, do everything in the block. Else execute the else block (alert).
Note that at the end of the if block we set input's value to empty with : input.value = "";
Declare the createDeleteButton and removeItem functions.
I don't change them because they work. Just have them all outside of addItem function and use const instead of var (scope reasons).
function createDeleteButton() {
const deleteButton = document.createElement("button");
deleteButton.classList.add("deleteButton");
deleteButton.textContent = "x";
deleteButton.addEventListener("click", removeItem);
return deleteButton;
}
function removeItem() {
const parent = this.parentNode.parentNode;
const child = this.parentNode;
parent.removeChild(child);
}
Here is the full code:
======================
const ul = document.getElementById("list");
const input = document.getElementById("task");
const addBtn = document.getElementById("my-button");
addBtn.onclick = addItem;
function addItem() {
if (input.value !== "") {
const listElement = document.createElement("li");
listElement.innerText = input.value;
listElement.appendChild(createDeleteButton());
ul.appendChild(listElement);
input.value = "";
} else {
alert("Please write something");
}
}
function createDeleteButton() {
const deleteButton = document.createElement("button");
deleteButton.classList.add("deleteButton");
deleteButton.textContent = "x";
deleteButton.addEventListener("click", removeItem);
return deleteButton;
}
function removeItem() {
const parent = this.parentNode.parentNode;
const child = this.parentNode;
parent.removeChild(child);
}
var input is defined as task.value. You need to get task and then change task.value
function todolist(){
var btn = document.getElementById('my-button').onclick = function() {
var ul = document.getElementById("list");
var li = document.createElement("li");
var input = document.getElementById('task').value;
if (input != '') {
addItem();
}
function createDeleteButton() {
var deleteButton = document.createElement('button');
deleteButton.classList.add("deleteButton");
deleteButton.textContent = "x";
deleteButton.addEventListener("click", removeItem);
return deleteButton;
}
function addItem() {
let listElement = document.createElement('li');
listElement.innerText = input;
listElement.appendChild(createDeleteButton());
if (input != "") {
list.appendChild(listElement)
} else alert("Please write something");
var task = document.getElementById('task')
console.log(task)
task.value = ""
input = "";
}
function removeItem() {
let parent = this.parentNode.parentNode;
let child = this.parentNode;
parent.removeChild(child);
}
}
}
todolist();
<h2>To-do list!</h2>
<div>
<input type="text" id="task" placeholder="Write task" />
<button id="my-button">Add task</button>
</div>
<ul id="list"></ul>
Instead of input.value = "" use input = ""

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);
}

Todo list wont refresh after pushing to array

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);
}

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