2

I'm trying to create a playlist of songs. To create the playlist I created an update function called checklist(). Which receives an array parameter called playlist of songs currently in the playlist. I do also want to add a removebutton which refers to every song in the playlist. However, adding the addEventListener in an iterative way only adds functionality to the last item iterated over. I guess the event listeners are overwritten every iteration and it is a common mistake but i can't seem to figure out how to fix it. Help is appreciated!

function checklist(playlist){
    for (var i = 0; i < playlist.length; i++){
         var j = i+1;
         document.getElementById("list").innerHTML += '<button id="removebtn'+j+'"></button>'
         document.getElementById("removebtn"+j).addEventListener("click", function () {
              delete playlist[i];
              console.log("deleted");
         });
    };
}

I already tried: fixing problems with closure as described here: JavaScript closure inside loops – simple practical example

I also tried to work a way around by adding code programmatically: Adding code to a javascript function programmatically

Kennos
  • 227
  • 1
  • 6
  • I think what you're looking for is [Array.splice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) – GMaiolo Jan 23 '20 at 16:47

3 Answers3

1

What you need is to use something like jQuery.index() to find the right element in the array. Since this is marked as a pure js question, I'll demonstrate how to do this

function getIndex(elem) {
  var parent = elem.parentNode;
  for (var x = 0; x < parent.childNodes; x++) {
    if (parent.childNodes[x] == elem) return x;
  }
  return -1; // shouldn't be possible, but good defensive coding
}

Then, in your removebtn function

     document.getElementById("removebtn"+j).addEventListener("click", function (e) {
        var index = getIndex(e.target);
          delete playlist[index];
          console.log("deleted");
     });
};

e.target may not be the correct node to get the index from (you may want e.target.parentNode), but the idea is that it gives you the relative position in the playlist

ControlAltDel
  • 33,923
  • 10
  • 53
  • 80
1

Everytime you change the innerHTML it reconstructs the descendants or child elements (even if you append it). Therefore, the listeners added previously will no longer exist.

Instead use appendChild.

const list = document.querySelector('#list');

function checklist(playlist){
    for (var i = 0; i < playlist.length; i++){
         var j = i+1;
         const button = document.createElement('button');
         button.id = "removebtn"+j;
         button.textContent = "Button"+j;
         button.addEventListener('click', function() {
            this.remove();
         });
         list.appendChild(button);
    };
}

checklist(["a", "b"]);
<div id="list"></div>
random
  • 7,756
  • 3
  • 19
  • 25
1

The closure solutions you already tried, combined with @random's advice about not using innerHTML, should work for you. For example, you can use let to define local scope:

function checklist(playlist){
    for (let i = 0; i < playlist.length; i++){
        let button = document.createElement("button");
        button.innerHTML = `Remove item ${i+1} - ${playlist[i]}`;
        button.addEventListener("click", e => {
            playlist.splice(i, 1);
            console.log("deleted", i, "playlist:", playlist);
            button.remove();
        });
        document.getElementById('list').appendChild(button);
    };
}
checklist(['one', 'two', 'three']);
<div id="list"></div>

However there is still an additional problem that after you remove an item from the playlist, the indices for later items will be set back by one and your buttons will no longer refer to the correct items. To correct this you could instead identify items in the playlist with an ID of some kind, or you could add a list of indices in your function:

function checklist(playlist){
  let indices = [];
  for (let i = 0; i < playlist.length; i++){
        indices.push(i);
        let button = document.createElement("button");
        button.innerHTML = `Remove item ${i+1} - ${playlist[i]}`;
        button.addEventListener("click", e => {
            let j = indices.indexOf(i);
            playlist.splice(j, 1);
            indices.splice(j, 1);
            console.log("deleted", i, "playlist:", playlist, "indices:", indices);
            button.remove();
        });
        document.getElementById('list').appendChild(button);
    };
}
checklist(['one', 'two', 'three']);
<div id="list"></div>
Stuart
  • 9,597
  • 1
  • 21
  • 30