Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript dynamic button to delete item by id and delete himself (infinite loop)

Tags:

javascript

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.

like image 406
nishi Avatar asked Jul 26 '20 05:07

nishi


2 Answers

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
  });
});
like image 142
Abito Prakash Avatar answered Sep 27 '22 16:09

Abito Prakash


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()
  })
})
like image 39
Robert Cooper Avatar answered Sep 27 '22 17:09

Robert Cooper