I'm trying to remove specific li elements, based off of which one has the x button clicked. Currently I'm having an error

"bZMQWNZvyQeA:42 Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'."

I am aware that this could mean that the paramater is null, but this dosn't make any sense to me. Chrome dev tools show that the onClick attribute is correctly exectuing removeItem, and passing in the idName as a parameter. How is this not working?

var note = 0;

function saveInfo() {
  var idName = "note" + note;
  //assign text from input box to var text, and store in local storage
  var input = document.getElementById('input').value;
  var text = localStorage.setItem(note, input);
  var list = document.createElement("li");
  var node = document.createTextNode(input);
  var removeBtn = document.createElement("button");

  list.setAttribute("id", idName);
  removeBtn.setAttribute("onClick", `removeItem(${idName})`);
  removeBtn.innerHTML = "X";
  list.appendChild(node);
  list.appendChild(removeBtn);
  document.getElementById("output").appendChild(list);

  note += 1;
}

function removeItem(name) {
  var parent = document.getElementById("output");
  var child = document.getElementById(name);
  parent.removeChild(child);
}

2 Answers

0
Terry On Best Solutions

In my comment, I suggested that you listen to click event bubbling from the removeBtn. In this case, all you need is to remove the onclick attribute assignment logic from your code, and instead give your removeButton an identifiable property, such as a class. Lets give it a class of delete-button:

var removeBtn = document.createElement("button");
removeBtn.classList.add('delete-button');
removeBtn.type = 'button';
removeBtn.innerHTML = 'X';

Then, you can listen to the click event at the level of #output, which is guaranteed to be present at runtime. When the event is fired, you simply check if the event target has the identifiable property, e.g. the remove-button class in our case:

output.addEventListener('click', function(e) {

  // GUARD: Do nothing if click event does not originate from delete button
  if (!e.target.matches('.remove-button')) {
    return;
  }

  // Delete parent node
  e.target.closest('li').remove();
});

If the click event did not originate from the remove button, we simply return and don't do anything else. Otherwise, we know that the button has been clicked, and we can then use Element.closest(), i.e. .closest('li') to retrieve the closest <li> parent node and delete it.

If you absolutely have to support IE11 (which in turn, does not support Element.closest()), you can also use Node.parentNode to access and delete the <li> element, assuming that your remove button is a direct child of the <li> element:

// Delete parent node
e.target.parentNode.remove();

See proof-of-concept below:

var rows = 10;
var output = document.getElementById('output');

for (var i = 0; i < rows; i++) {
  var list = document.createElement('li');
  var node = document.createTextNode('Testing. Row #' + i);
  var removeBtn = document.createElement("button");
  removeBtn.classList.add('remove-button');
  removeBtn.type = 'button';
  removeBtn.innerHTML = 'X';
  
  list.appendChild(node);
  list.appendChild(removeBtn);
  
  output.appendChild(list);
}

output.addEventListener('click', function(e) {

  // GUARD: Do nothing if click event does not originate from delete button
  if (!e.target.matches('.remove-button')) {
    return;
  }
  
  e.target.closest('li').remove();
});
<ul id="output"></ul>

0
trincot On

The issue is that you have missing quotes around the id that you pass to removeItem:

removeBtn.setAttribute("onClick", `removeItem(${idName})`);

This should be:

removeBtn.setAttribute("onClick", `removeItem('${idName}')`);

Better pattern

It is better practice to bind the click handler without relying on string evaluation of code, and without needing to create dynamic id attribute values:

removeBtn.addEventListener("click", () => removeItem(list));

And then the function removeItem should expect the node itself, not the id:

function removeItem(child) {
  child.parentNode.removeChild(child);
}

You can remove the following code:

var idName = "note" + note;
list.setAttribute("id", idName);