I am trying to push a ingredient to a list of array. This ingredient is an object and has its id
from uuid
or any library and an ingredient value of whatever the user types on the input.
I wrote the simplest example of it in a single function so its clear to understand the core of my doubt.
How can I delete the item by id and re-render, if the button is part of the renderization.
I could create a render function and invoke it inside the remove button. The problem is that the button would be part of that function.
Code
let ingredients = []
document.querySelector('#ingredients-input').addEventListener('change', (e) => {
e.preventDefault()
const id = uuid()
ingredients.push({
id: id,
title: e.target.value
})
const ingredientUl = document.createElement('p')
const removeButton = document.createElement('button')
ingredientUl.textContent = e.target.value
removeButton.textContent = 'remove'
document.querySelector('#ingredients').append(ingredientUl, removeButton)
removeButton.addEventListener('click', (e) => {
const ingredientIndex = ingredients.findIndex(el => el.id === id)
ingredients.splice(ingredientIndex, 1)
})
})
<div id="ingredients"></div>
<input id="ingredients-input">
CodePen: https://codepen.io/notnishi/pen/QWyPdLL
Any help would be appreciated.
Instead of re rendering, you can delete the p tag and the remove button from#ingredients
. Deleting only the specific elements from DOM is much more efficient than rerendering the whole ingredients list for just one change.
The remove button can be removed by using ChildNode.remove() and for deleting the ingredientUl, we can iterate over the child list of #ingredients
and find the item to removed first and then delete it using Node.removeChild()
let ingredients = [];
const ingredientsNodeList = document.querySelector("#ingredients");
document.querySelector("#ingredients-input").addEventListener("change", (e) => {
e.preventDefault();
const id = uuid();
ingredients.push({
id: id,
title: e.target.value,
});
const ingredientUl = document.createElement("p");
const removeButton = document.createElement("button");
ingredientUl.textContent = e.target.value;
ingredientUl.id = id;
e.target.value = ""; // Clearing the input after entering it to ingredients list
removeButton.textContent = "remove";
ingredientsNodeList.append(ingredientUl, removeButton);
removeButton.addEventListener("click", (e) => {
const ingredientIndex = ingredients.findIndex((el) => el.id === id);
ingredients.splice(ingredientIndex, 1);
const itemToDelete = [...ingredientsNodeList.children].find(el => el.id === id);
ingredientsNodeList.removeChild(itemToDelete); // Removing ingredientUl
e.target.remove(); // Deleting removeButton
});
});
Similar solution to one of the previous answer, but I would wrap your added items in a div
with an attribute that holds the value of your uuid, so you can easily find the uuid when click the remove button and then you can manually update the values in your ingredients
array and also manually remove the wrapping div
from the DOM.
CodePen demo
let ingredients = []
document.querySelector('#ingredients-input').addEventListener('change', (e) => {
e.preventDefault()
const id = uuid()
ingredients.push({
id: id,
title: e.target.value
})
const wrapper = document.createElement('div')
wrapper.setAttribute('data-uuid', id)
const ingredientUl = document.createElement('p')
const removeButton = document.createElement('button')
ingredientUl.textContent = e.target.value
removeButton.textContent = 'remove'
wrapper.append(ingredientUl, removeButton)
document.querySelector('#ingredients').append(wrapper)
removeButton.addEventListener('click', (e) => {
const wrapper = e.target.closest('div[data-uuid]');
const wrapperUuid = wrapper.getAttribute('data-uuid');
// Filters out the ingredient associated with the clicked remove button
ingredients = ingredients.filter(ingredient => ingredient.id !== wrapperUuid)
// Removes element from dom
wrapper.remove()
})
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With