Taking out specific value from array with objects - javascript

Making a todo app, but got stucked at deleting specific value in array, what am i doing wrong and how should i correct it? splice acts same as shift method.
Also is there any other way or data structure that can be used for todo app.
const form = document.querySelector("form");
const todoInput = document.querySelector(".todoInput");
const list = document.querySelector(".renderListHere");
const todoList = [];
form.addEventListener("submit", (event) => {
event.preventDefault();
const text = todoInput.value.trim();
if (text === "") {
console.log("enter something");
} else {
addTodo(text);
todoInput.value = "";
}
});
const addTodo = (text) => {
const todo = {
id: Date.now(),
text,
};
todoList.push(todo);
renderTodo(todo);
};
const renderTodo = ({ text, id }) => {
const li = document.createElement("li");
li.classList.add("todoListItems");
li.innerHTML = `
<span> ${text} </span>
<button id="${id}" class="del-btn"> x
</button>
`;
list.append(li);
};
list.addEventListener("click", (event) => {
if ((event.target.className = "del-btn")) {
const arr = todoList.filter(
(item) => item.id === parseInt(event.target.id)
);
todoList.splice(arr, 1);
}
});

I think you misunderstood the filter method.
The Array.filter() function returns a new array, so in your case, you could use:
todoList = todoList.filter(
(item) => item.id !== parseInt(event.target.id)
);
So you are filtering todoList with only the items with id different than event.target.id, and applying the result to the same todoList variable.

const arr = todoList.filter(
(item) => item.id !== parseInt(event.target.id)
);
}
its return new array
you do not need splice()

Related

Why isn't my classlist removing when using this constructor function

So i'm trying to remove my classlist of selected items when they are clicked again:
const todo = []
const elements = []
const todoList = document.getElementById('todoList')
const addItem = document.getElementById('textBox')
const submit = document.getElementById('submit')
const todoListItem = document.querySelectorAll('.todoListItem')
const todoListItemArr = Array.from(todoListItem)
const deleteItem = document.getElementById('deleteButton')
let selected
class Div {
constructor(content) {
this.content = content
}
addToScreen(){
const element = document.createElement('div')
element.innerHTML = this.content
element.classList.add('todoListItem')
todoList.appendChild(element)
}
select(){
const element = document.querySelectorAll('.todoListItem')
element.forEach(item => {
item.addEventListener('click', () => {
item.classList.add('selectedItem')
selected = item
})
})
}
deselect() {
const elements = document.querySelectorAll('.selectedItem')
elements.forEach(item => {
item.addEventListener('click', () => {
item.classList.remove('selectedItem')
})
})
}
}
function createItem() {
const todoItem = new Div(addItem.value)
todoItem.addToScreen()
todo.push(todoItem.content)
console.log(todo)
addItem.value = ''
todoItem.select()
todoItem.deselect()
}
addItem.addEventListener('keypress', (e) => {
if(e.key == 'Enter') {
createItem()
}
})
submit.addEventListener('click', createItem)
also I'm trying to add a delete function, but everytime i try to remove the element in the constructor by adding a delete function, my delete button does nothing, i've removed those features for now.

how to delete dynamic created li by id?

I am a beginner in javascript and I have li which is dynamically inserted by the user and is saved in an array of objects, it has its dynamic random id and I want when the user presses the element, it returns the id of the li and compare it with the movie id so I can find the index and then remove it from the array and remove the li. Thank you for your help in advance.
let n;
function deleteFilm() {
const len = listRoot.children.length;
for (let i = 0; i < len; i++) {
const element = listRoot.children[i]; // listRoot is ul
element.addEventListener('click', () => {
const idToRemove = +element.id;
index = movies.map((object) => object.movieId).indexOf(idToRemove);
console.log(index);
n = index;
});
}
console.log(n);
movies.splice(n, 1);
listRoot.children[n].remove();
if (movies[0] === undefined) {
entryText.style.display = 'block';
}
deleteModal.classList.remove('visible');
blackDrop.classList.remove('visible');
}
acceptButton.addEventListener('click', deleteFilm);
Don't assign all over again clicks (specially not inside a for loop).
Your basic logic could be simplified to this couple of lines:
const movies = [
{id:123, title:"Lorem"},
{id:456, title:"Ipsum"},
{id:789, title:"Dolor"},
];
// Retrieve a movie from array by its ID
const getMovie = (id) => movies.find(mov => mov.id === id);
// Remove a movie Object from array
const deleteMovie = (id) => movies.splice(movies.indexOf(getMovie(id)), 1);
// Task:
const movieId = 456; // The movie ID to delete
deleteMovie(movieId);
console.log(movies); // Test
Then, to delete elements from the DOM, knowing the ID was i.e: 456 all you need is to target the elements that have the data-movieid="456" like:
document.querySelectorAll(`[data-movieid="${movieId}"]`).forEach(el => el.remove());
Example:
// Utility functions:
const ELNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
const ELS = (sel, parent) => (parent || document).querySelectorAll(sel);
const EL = (sel, parent) => (parent || document).querySelector(sel);
// Task:
const getMovie = (id) => movies.find(mov => mov.id === id);
const deleteMovie = (id) => movies.splice(movies.indexOf(getMovie(id)), 1);
const createMovie = (movie) => {
const EL_li = ELNew("li", {
className: "list-item movie",
textContent: movie.title,
onclick() {
deleteMovie(movie.id);
EL_li.remove();
ELS(`[data-movieid="${movie.id}"]`).forEach(el => el.remove());
}
});
EL("#movies").append(EL_li);
};
const movies = [
{id:123, title:"Lorem"},
{id:456, title:"Ipsum"},
{id:789, title:"Dolor"},
];
movies.forEach(createMovie);
Click to delete a movie:
<ul id="movies" class="list"></ul>
<div data-movieid="456">If you click on Ipsum I will be removed too!</div>

Local storage not deleting data until page is refreshed

I am pretty much new to Javascript and working on this simple ToDo app that uses local storage to persist data. However, the Delete function can only delete from local storage when refreshed the page. What could be causing this bug? I have attached my code below
I tried commenting on the e.preventDefault() on the form but the page kept on reloading when a task is submitted.
// Selectors
const ul = document.querySelector('.todo-list');
const todoContainer = document.querySelector('.todo-container');
const clearButton = document.createElement('button');
clearButton.classList.add('clear-button');
clearButton.textContent = 'Clear all Completed';
const form = document.querySelector('.form');
const taskInput = document.querySelector('.todo-input');
let todoTasks = JSON.parse(localStorage.getItem('todo')) || [];
let id = todoTasks.length + 1;
const createElement = ({ description, completed = true, index }) => {
const todoItem = document.createElement('li');
todoItem.classList.add('todo-list-item');
todoItem.setAttribute('id', index);
todoItem.innerHTML = `
<input type="checkbox" class="check" value="${completed}">
<button class="hidden" name="${index}"></button>
<span class="todo-item">${description}</span>
<button name='eclips'><i class="fas fa-ellipsis-v"></i></button>
<button name='delete'><i class="fas fa-trash"></i></button>
`;
ul.appendChild(todoItem);
todoContainer.appendChild(ul);
todoContainer.appendChild(clearButton);
};
// get each todo task
todoTasks.forEach(createElement);
// Function that add todo
const addTask = (description, completed, ind) => {
const input = taskInput.value;
todoTasks.push({
description: input,
completed: false,
index: ind,
});
localStorage.setItem('todo', JSON.stringify(todoTasks));
return { description, completed, ind };
};
const checkTodo = (e) => {
const lineText = e.target.nextElementSibling.nextElementSibling.nextElementSibling;
if (lineText.style.textDecoration === 'line-through') {
lineText.style.textDecoration = 'none';
} else {
lineText.style.textDecoration = 'line-through';
lineText.classList.toggle('completed');
}
};
// Function that delete todo
const handleDeleteAndCheck = (e) => {
const item = e.target;
if (e.target.classList[1] === 'fa-trash') {
const todo = item.parentElement.parentElement;
const targetId = item.parentElement.parentElement.id;
todoTasks = todoTasks.filter((task) => task.index !== +targetId);
localStorage.setItem('todo', JSON.stringify(todoTasks));
todo.remove();
}
if (e.target.classList === 'check') {
checkTodo();
}
};
// Function that Edit todo
const editTask = (e) => {
const item = e.target;
if (item.classList[0] === 'todo-item') {
item.contentEditable = true;
item.style.display = 'block';
}
};
const check = document.querySelectorAll('.fa-check-square');
const index = document.querySelectorAll('#index');
form.addEventListener('submit', (e) => {
e.preventDefault();
const input = taskInput.value;
const checkValue = check.value;
// const indexValue = index.value;
const newTask = addTask(input, checkValue, id);
createElement(newTask);
// location.reload();
taskInput.value = '';
id += 1;
});
ul.addEventListener('click', handleDeleteAndCheck);
ul.addEventListener('click', editTask);
Here please change the statement with this in your handleDeleteAndCheck() method. It will remove the local storage at that time.
localStorage.removeItem('todo');

How would I delete the same item from UI and localStorage

I am trying to remove an item of a ToDo app both from the UI and localStorage when its delete button is clicked. I can remove the item from the UI when e.target.calssName === 'delete'. However, I can't access the index of the array in localStorage and delete the same item from there as well. Here is the code I am working on. Any help would be highly appreciated.
const addTask = document.querySelector('#addItem');
const inputForm = document.querySelector('#inputForm');
const saveButton = document.querySelector('#saveButton');
const output = document.querySelector('#output');
// Add task button
const addItem = () => {
inputForm.style.display = 'block';
taskInput.focus();
};
// Add and update item to localstorage
const checkStorage = () => {
const input = document.querySelector('#taskInput');
if(input.value) {
const task = {
name: input.value
};
if(localStorage.getItem('tasks') === null) {
const tasks = [];
tasks.push(task)
localStorage.setItem('tasks', JSON.stringify(tasks));
} else {
const tasks = JSON.parse(localStorage.getItem('tasks'));
tasks.push(task);
localStorage.setItem('tasks', JSON.stringify(tasks));
}
} else {
return false;
};
};
// Display item on UI
const displayItem = () => {
const tasks = JSON.parse(localStorage.getItem('tasks'));
if(tasks !== null) {
const html = tasks.map((task, index) => {
return `
<ul class="item" id="${index}">
<li>${task.name}</li>
<button class="edit" id="${index}">Edit</button>
<button class="delete" id="${index}">Delete</button>
</ul>
`
}).join('');
output.innerHTML = html;
} else {
checkStorage();
};
};
// Save the task and display the item
const saveTask = (e) => {
e.preventDefault();
checkStorage();
displayItem();
inputForm.reset();
inputForm.style.display = 'none'
};
// Delete item from UI and localStorage
const deleteItem = (e) => {
const tasks = JSON.parse(localStorage.getItem('tasks'));
// Not sure how to loop throuh the tasks array and delete the item which is deleted from the UI
}
document.addEventListener('click', deleteItem);
document.addEventListener('onload', displayItem())
saveButton.addEventListener('click', saveTask);
addTask.addEventListener('click', addItem);
You can use Array#splice to delete an element from an array.
const deleteItem = (e) => {
if(e.target.classList.contains("delete")){
const tasks = JSON.parse(localStorage.getItem('tasks'));
tasks.splice(+(e.target.getAttribute("id")), 1);
localStorage.setItem('tasks', JSON.stringify(tasks));
}
}
many different ways to do this.best way is to user filter. More about filter
var tasks = JSON.parse(localStorage.getItem('tasks'));
tasks = tasks.filter(task=> "your condition");
localStorage.setItem('tasks', JSON.stringify(tasks));
Note: if you have the the id of the task it should go like this.
tasks = tasks.filter(task=> task.id != id );

Rendering array items after making changes to its properties

I'm having an issue re-rendering items in an array after changes are made to elements in the array. Whether I add by pushing or remove by splicing, when the array is rendered again on the page, it like more items are being added to the array. So if I push onto the array, the item is added, but the old items are then duplicated into the array. Something similar happens when I remove items. The item looks to be removed, but the elements that were in the array show on the page, they are then duplicated and the item that was spliced is gone.
I'm trying to avoid a location.reload('/edit.html') to refresh the page. Kind of cheating. It seems to work, but I'm trying to get the page to refresh with my renderIngredients function. The toggleIngredient function is also duplicating the list of items when I check an item.
import { initializeEditPage, generateLastEdited } from './views'
import { updateRecipe, removeRecipe, saveRecipes, getRecipes, createIngredient } from './recipes'
const titleElement = document.querySelector('#recipe-title')
const bodyElement = document.querySelector('#recipe-body')
const removeElement = document.querySelector('#remove-recipe')
const addElement = document.querySelector('#add-recipe')
const dateElement = document.querySelector('#last-updated')
const addIngredient = document.querySelector('#new-ingredient')
const recipeStatus = document.querySelector('#recipe-status')
const recipeId = location.hash.substring(1)
const recipeOnPage = getRecipes().find((item) => item.id === recipeId)
titleElement.addEventListener('input', (e) => {
const recipe = updateRecipe(recipeId, {
title: e.target.value
})
dateElement.textContent = generateLastEdited(recipe.updatedAt)
})
bodyElement.addEventListener('input', (e) => {
const recipe = updateRecipe(recipeId, {
body: e.target.value
})
dateElement.textContent = generateLastEdited(recipe.updatedAt)
})
addElement.addEventListener('click', () => {
saveRecipes()
location.assign('/index.html')
})
removeElement.addEventListener('click', () => {
removeRecipe(recipeId)
location.assign('/index.html')
})
addIngredient.addEventListener('submit', (e) => {
const text = e.target.elements.text.value.trim()
e.preventDefault()
if (text.length > 0) {
createIngredient(recipeId, text)
e.target.elements.text.value = ''
}
renderIngredients(recipeId)
saveRecipes()
//location.reload('/edit.html')
})
const removeIngredient = (text) => {
const ingredientIndex = recipeOnPage.ingredients.findIndex((ingredient)=> ingredient.text === text)
if (ingredientIndex > -1) {
recipeOnPage.ingredients.splice(ingredientIndex, 1)
}
saveRecipes()
renderIngredients(recipeId)
//location.reload('/edit.html')
}
const toggleIngredient = (text) => {
const ingredient = recipeOnPage.ingredients.find((ingredient) => ingredient.text === text)
if (ingredient.included) {
ingredient.included = false
} else {
ingredient.included = true
}
//location.reload('/edit.html')
}
const ingredientSummary = (recipe) => {
let message
const allUnchecked = recipeOnPage.ingredients.every((ingredient) => ingredient.included === false)
const allChecked = recipeOnPage.ingredients.every((ingredient) => ingredient.included === true)
if (allUnchecked) {
message = `none`
} else if (allChecked) {
message = `all`
} else {
message = `some`
}
return `You have ${message} ingredients for this recipe`
}
const generateIngredientDOM = (ingredient) => {
const ingredientEl = document.createElement('label')
const containerEl = document.createElement('div')
const checkbox = document.createElement('input')
const ingredientText = document.createElement('span')
const removeButton = document.createElement('button')
recipeStatus.textContent = ingredientSummary(recipeOnPage)
// Setup ingredient container
ingredientEl.classList.add('list-item')
containerEl.classList.add('list-item__container')
ingredientEl.appendChild(containerEl)
// Setup ingredient checkbox
checkbox.setAttribute('type', 'checkbox')
checkbox.checked = ingredient.included
containerEl.appendChild(checkbox)
// Create checkbox button in ingredient div
checkbox.addEventListener('click', () => {
toggleIngredient(ingredient.text)
saveRecipes()
renderIngredients(recipeId)
})
// Setup ingredient text
ingredientText.textContent = ingredient.text
containerEl.appendChild(ingredientText)
// Setup the remove button
removeButton.textContent = 'remove'
removeButton.classList.add('button', 'button--text')
ingredientEl.appendChild(removeButton)
// Create remove button in ingredient div
removeButton.addEventListener('click', () => {
removeIngredient(ingredient.text)
saveRecipes()
renderIngredients(recipeId)
})
return ingredientEl
}
const renderIngredients = (recipeId) => {
// Grab the ingredient display from the DOM
const ingredientList = document.querySelector('#ingredients-display')
const recipe = getRecipes().find((item) => {
return item.id === recipeId
})
// Iterate through the list of ingredients on the page and render all items from recipeDOM
recipe.ingredients.forEach((ingredient) => {
const recipeDOM = generateIngredientDOM(ingredient)
ingredientList.appendChild(recipeDOM)
})
}
renderIngredients(recipeId)
I believe the issue stems from my renderIngredients function but I can't figure out how to fix it. Again, when I refresh the page, the results I want display, but I want to avoid using location.reload. I'm expecting the removeIngredient function to remove the ingredient with a button click and the page refreshes with the renderIngredients function. Also expecting the toggleIngredient function to just display a checkbox next to the ingredient I checked off, but that's not what's happening. The Same thing is happening when I use the addIngredient function, the ingredient is being added, but the ingredient that was already on the page is being duplicated.
I guess you want to clear the list before adding the elements again:
ingredientList.innerHTML = "";

Categories