Making a todo list for a class project - able to get the item to "add" and append to the list, but when you refresh the page, the item returns as undefined. Checking localStorage.todo, I see it pulling part of the values I need, but aren't in use yet, but not the actual innerText. I started adding bits for a line-through to check off completed items and a remove button, but that was commented out and hopefully isn't impacting things - was trying to isolate the error.
I'm guessing my issue is something in the localStorage.setItem, but I'm not sure what it would be. I've tried changing the newTodo.innerText value to other variables in use but nothing returns. Below returns an input value I enter, but again, is lost when the page refreshes.
const todoForm = document.querySelector('#todoForm');
const todoList = document.querySelector('#todoList');
let todoItem = document.querySelector('#todoItem');
// Pull from storage
const savedList = JSON.parse(localStorage.getItem('todo')) || [];
for (let i = 0; i < savedList.length; i++)
{
let newTodo = document.createElement('li');
newTodo.innerText = savedList[i].task;
todoList.appendChild(newTodo);
}
// Add Item
todoForm.addEventListener('submit', function(e){
e.preventDefault();
let newTodo = document.createElement('li');
let newItem = document.querySelector('#todoItem').value;
newTodo.innerText = newItem;
todoForm.reset();
todoList.appendChild(newTodo);
// Clear Form Field on submit and storage
savedList.push({ task: newTodo.innertext, isCompleted: false });
localStorage.setItem('todo', JSON.stringify(savedList));
});
// Add Remove Button
// let removeItem = document.createElement('button');
// removeItem.innerText = "Remove";
// newTodo.append(removeItem);
// Strike through item or remove item
// todoList.addEventListener('click', function(e){
// if (e.target.tagName === 'BUTTON'){
// e.target.parentElement.remove();
// }
// else if (e.target.tagName === 'LI'){
// e.target.style.textDecoration = 'line-through';
// }
// });
camelcase is wrong not innertext but innerText
savedList.push({ task: newTodo.innerText, isCompleted: false })
Related
I am trying to create a Todo list and wonder how I would go about keeping the completed todos from vanishing each time I add a new task. I am aware that this is happening because I clear my div each time a new task is added, however I am unsure on how to proceed for me to keep using arrays on this and still show completed tasks along with new tasks.
Codepen: https://codepen.io/martinkariuki7-the-looper/pen/OJvQXRW
const newToDo = document.getElementById('todo-new')
const addBtn = document.getElementById('addToDo')
const main= document.getElementById('toDoList')
const taskDone = document.getElementsByClassName('task')
let toDoList = []
// populate to do list
function populateToDo(){
let todo = newToDo.value
todo !== null && todo !== '' ? toDoList.push(todo) : alert('You must write something')
updateDOM()
}
//Update DOM
function updateDOM(){
// Clear main div
main.innerHTML = `<h1>To do list </h1>`
// Show tasks on the front
toDoList.forEach(item =>{
let task = document.createElement('div')
task.classList.add('task')
task.innerHTML = `<label><input type="checkbox" >${item}</label><br>`
main.append(task)
task.addEventListener('change', () => task.classList.toggle('task-complete'))
newToDo.value = ''
})
}
// Event listeners
// Add Tasks
addBtn.addEventListener('click', populateToDo)
newToDo.addEventListener('keydown', (e) => {
if(e.code === 'Enter'){
e.preventDefault()
populateToDo()
}
})
If you want to use an array to update the list, you need to compare what you have with what will be changed.
If there aren't items with the same text, you could do something like this:
function updateNodes()
{
const nodesMap = new Map();
[...document.querySelectorAll('div label')]
.forEach(node => {
nodesMap.add(node.innerText, node);
});
const nodesUpdate = [];
toDoList.forEach(item => {
if (nodesMap.has(item)) {
nodesUpdate.push(nodesMap[item]);
} else {
const newNode = document.createElement('div');
newNode.classList.add('task');
newNode.innerHTML = `<label><input type="checkbox" >${item}</label>`;
nodesUpdate.push(newNode);
}
});
return nodesUpdate;
}
Otherwise, each item (or task) needs to have an identification. Otherwise, if there are two items with the same name, you won't know which one was removed or moved. Besides, without identifications, you can't know if an item was renamed.
The code here was not tested and, of course, you need to adapt it to your needs.
I'm trying to make a kind of catalog and once someone presses the "show more" button it should show the description from the corresponding data I got from a API/JSON file. I tried using e.target but I'm stuck at this point.
function getCocktailItemsSuccessHandler(data) {
for(item of data){
const cardDiv = document.createElement("div")
cardDiv.classList.add("card")
cocktailGallery.appendChild(cardDiv)
const nameCocktail = document.createElement("h2")
nameCocktail.classList.add("listName")
nameCocktail.innerText = item.name
cardDiv.appendChild(nameCocktail)
const img = document.createElement("img")
img.src = item.image
cardDiv.appendChild(img)
const detailsButton = document.createElement("button")
detailsButton.classList.add("detailsButton")
detailsButton.innerHTML = 'More'
detailsButton.addEventListener('click', detailsClickHandler)
cardDiv.appendChild(detailsButton)
}
}
function detailsClickHandler(e) {
let details = e.target
detailsContainer.innerHTML = item.
}
If I am understanding your structure, you can store the incoming data in a variable that you can reference after the fact. See the lines with the // <--- add this line and then the whole detailsClickHandler function.
Another option of course is to create a div and insert the description text in it, hide it with display:none and toggle it on with the button click. The way I've presented below is more dynamic, but not neccesarily better.
let theData = [] // <-- add this line
function getCocktailItemsSuccessHandler(data) {
theData = data; // <-- add this line
for (item of data) {
const cardDiv = document.createElement("div")
cardDiv.classList.add("card")
cardDiv.setAttribute('data-id', item.id); // <-- add this line
cocktailGallery.appendChild(cardDiv)
const nameCocktail = document.createElement("h2")
nameCocktail.classList.add("listName")
nameCocktail.innerText = item.name
cardDiv.appendChild(nameCocktail)
const img = document.createElement("img")
img.src = item.image
cardDiv.appendChild(img)
const detailsButton = document.createElement("button")
detailsButton.classList.add("detailsButton")
detailsButton.innerHTML = 'More'
detailsButton.addEventListener('click', detailsClickHandler)
cardDiv.appendChild(detailsButton)
}
}
function detailsClickHandler(e) {
let details = e.target
// get the relative id
let id = details.closest('.card').dataset.id;
// get the item from the object
let item = theData.filter(item => item.id.toString().trim() === id.toString().trim());
detailsContainer.innerHTML = item[0].description; // or whatever field the html is in
}
You can pass the item dynamically to the detailsClickHandler() function and then use it there to update details.
detailsButton.addEventListener('click', (event) => detailsClickHandler(event, item))
function detailsClickHandler(event, item) {
detailsContainer.innerHTML = item
}
I have a form that places a user's inputs onto the DOM, as well as removing them. The issue is if someone presses the remove button when no inputs are logged onto the DOM it will throw an error to the console. I attempted an IF statement but it doesn't work, The code which I have used is: (h6_w === "" || h6_w == null) { return false; }.
The error which the console is throwing is: app.js:210 Uncaught TypeError: Cannot read property 'remove' of undefined at HTMLAnchorElement. <anonymous(app.js:210)
// Delete From The Dom.
const delBtn = document.querySelector('.del-btn');
delBtn.addEventListener('click', (e) => {
// Remove Form Input.
let h6_w = document.querySelectorAll('.h6_weight_class');
// Empty String.
if (h6_w === "" || h6_w == null) {
return false;
} else {
// Remove Child
h6_w[h6_w.length - 1].remove();
};
});
// Add User's input To The Dom.
const addDom = document.querySelector('.add-dom');
const wForm = document.querySelector('.weight-form');
wForm.addEventListener('submit', (e) => {
e.preventDefault();
// Get Input Value.
const value = wForm.querySelector('input[type="number"]').value;
const value1 = wForm.querySelector('input[type="text"]').value;
// Create Elements.
const h6_weight = document.createElement('h6');
// Adding Id.
h6_weight.classList.add('h6_weight_class');
// Add Content.
h6_weight.textContent = value + value1;
// Append To Dom.
addDom.appendChild(h6_weight);
});
I've just started learning about localstorage in javascript, so I'm working on a project to practice. My program is a bookmark manager. The user can input a name and the url for there webpage, then it'll be stored and prepended to a div. If the user clicks on a trash icon, I want to find the url link that is associated with that trash icon, then remove it from localstorage.
Here's a link to the code and a demo.
LINK
JS
// READY
$(document).ready(() => {
// when user clicks on submit
$(".submit").on("click", addItem);
// when user clicks on delete
$(".fa-trash").on("click", deleteItem);
// show bookmarks
showBookmarks();
})
// ADD ITEM
let addItem = (e) => {
// get values from inputs
let name = $(".name").val();
let link = $(".url").val();
// stores bookmarks
let bookmark = {
name: name,
url: link
};
// bookmark varification
// if theres nothing in bookmarks
if(localStorage.getItem("bookmarks") == null) {
// init array
let bookmarks = [];
// add to array
bookmarks.push(bookmark);
// set to local storage
localStorage.setItem("bookmarks", JSON.stringify(bookmarks));
} else { // if theres something in bookmarks
// get from local storage
let bookmarks = JSON.parse(localStorage.getItem("bookmarks"));
// add bookmark to array
bookmarks.push(bookmark);
// reset back to local storage
localStorage.setItem("bookmarks", JSON.stringify(bookmarks));
}
}
// SHOW BOOKMARKS
let showBookmarks = () => {
// get from local storage
let bookmarks = JSON.parse(localStorage.getItem("bookmarks"));
// loop through local storage data
for(let i = 0; i < bookmarks.length; i++) {
let name = bookmarks[i].name;
let url = bookmarks[i].url;
// append bookmarks
$(".show").prepend(`
<div class="bookmark">
${name}
<i class="fa fa-trash fa-lg" aria-hidden="true"></i>
</div>
`);
}
}
// DELETE ITEM
let deleteItem = (url) => {
// get bookmarks from localstorage
let bookmarks = JSON.parse(localStorage.getItem("bookmarks"));
// loop through bookmarks
for(let i = 0; i < bookmarks.length; i++) {
if(bookmarks[i].url == url) {
// remove from array
bookmarks.splice(i, 1);
}
}
// reset local storage
localStorage.setItem("bookmarks", JSON.stringify(bookmarks));
showBookmarks();
}
$(".fa-trash").on("click", deleteItem);
Should be
$(document).on("click", '.fa-fresh', deleteItem);
You are using the method for storing, not for removing. Your code should be like
localStorage.removeItem("bookmarks");
you can find more info here
You are never passing the url, but you can derive it from event object like this:
let deleteItem = (event) => {
// get bookmarks from localstorage
var a = $(event.target).siblings("a");
var url = $(a).attr("href");
let bookmarks = JSON.parse(localStorage.getItem("bookmarks"));
// loop through bookmarks
for(let i = 0; i < bookmarks.length; i++) {
if(bookmarks[i].url == url) {
// remove from array
console.log("removing i");
bookmarks.splice(i, 1);
}
}
// reset local storage
localStorage.setItem("bookmarks", JSON.stringify(bookmarks));
showBookmarks();
}
Use this localStorage.removeItem(key);
I ran into an issue where localStorage.removeItem('myItem'); was not truly deleting the item.
I found that if I added
window. before actually solved the issue:
window.localStorage.removeItem('myItem');
I have two function to saveItem() and other to loadItem(); but I need see my items "when I Refresh the Page", I am using localStorage to save data doing of this a JSON.
var input = document.getElementById('input');
function newItem(list, itemText){
var item = document.createElement('li');
item.className = 'item';
item.innerText = itemText;
list.appendChild(item);
saveItem();
}
input.onkeyup = function(evt){
var key = evt.keyCode || evt.whitch;
if(key == 13){
itemText = input.value;
console.log('createITem');
if(!itemText || itemText == '' || itemText == ' '){
return false;
}
newItem(document.getElementById('ul'), itemText);
}
}
function saveItem(){
var items = document.querySelector('li.item');
var data = Array.prototype.map.call(items, function(item){
return [item.innerHTML];
});
localStorage.setItem('data', JSON.stringify(data));
}
function loadItem(){
var items = JSON.parse(localStorage.getItem('data'));
if(!items){
return;
}
Array.prototype.map.call(items, function(item){
return newItem(document.getElementById('content-memo'), item[0]);
});
}
loadItem();
Are you sure saveItem is working? Your code shows you calling loadItem, but it doesn't show you calling saveItem. In any case, that's where your problem is.
If you open your Dev Tools pane and inspect localStorage (or from the console, see if localStorage.data is defined), you should see if it's working properly. If not, then of course loadItem won't work as expected.
In order to map all li.items, you have to change the line from:
var items = document.querySelector('li.item');
To this:
var items = document.querySelectorAll('li.item');
querySelector will only return the first result as a DOM object, and you can't call Array.prototype.map on it. You need an array-like object. queryItemSelectorAll gives you that.
As it stands, the Array.prototype.map call in saveItem returns an empty array. So that's what gets set in localStorage.data - and thus what gets returned to the map function in loadItem.
Aside from that, are you having other troubles?