0

I have a JS code and I am trying to make a button delete its parent element, it works, I could remove the second element then the first, but when I remove the first one, I can no longer delete the second one, it returns "btn[i] undefined".

let btn = document.getElementsByClassName('remove');
let div = document.getElementsByClassName('image');

//console.log(btn.length);

for (let i = 0; i < btn.length; i++) {
  btn[i].onclick = function() {
    btn[i].parentElement.remove();
  }
}
<div class="image">
  <img alt="First">
  <button class="remove">X</button>
</div>
<div class="image">
  <img alt="Second">
  <button class="remove">X</button>
</div>
j08691
  • 204,283
  • 31
  • 260
  • 272
Mahide
  • 5
  • 3
  • @Teemu well I could delete them in reverse, second then first, it works, but when I delete the first one, then second that's where the problem occurs, which I don't get. Both buttons have their own parent element. – Mahide Jul 19 '22 at 15:33
  • `document.getElementsByClassName` returns a live HTMLCollection. If you remove an element from the page then it also gets removed from the collection messing up your indexes. – phuzi Jul 19 '22 at 15:38
  • Closely related: [Strange behavior when iterating over HTMLCollection from getElementsByClassName](/q/15562484/4642212). – Sebastian Simon Dec 28 '22 at 19:57

1 Answers1

1

getElementsByClassName returns an HTMLCollection which updates dynamically as the DOM changes. This means, when you remove a button that was matched by your query selector, the collection updates dynamically and the array indices of each element change.

You start with two buttons with indexes 0 and 1. When you remove the 0th element, all subsequent elements "shift down" and occupy the space you just freed up. This means the button that was at btn[1] is now at btn[0], and when you attempt to run btn[1].parentElement..., you're access outside the bounds of the collection

In order for your code to work, you need to access the element that was clicked, not the original array of btn elements:

let btn = document.getElementsByClassName('remove');
let div = document.getElementsByClassName('image');

//console.log(btn.length);

for (let i = 0; i < btn.length; i++) {
  btn[i].onclick = function(event) {
    event.target.parentElement.remove();
  }
}
<div class="image">
  <img alt="First">
  <button class="remove">X</button>
</div>
<div class="image">
  <img alt="Second">
  <button class="remove">X</button>
</div>
user229044
  • 232,980
  • 40
  • 330
  • 338
  • I see, that explanation makes a lot of sense, I was thinking about it, that after deleting the first element it would turn the 2nd element's index to [0], and since my loop increases the value of i, my remove function would be pointing at a non existing element, hence it's undefined, but I couldn't think of a way to turn the logic into code. I didn't know that you could write the function that way to point it correctly. I am fairly new to JS, thank you so much for your help and explanation. – Mahide Jul 19 '22 at 16:00