skip to Main Content

I’m making a dynamic list where users can click a button to remove inputs. When creating new inputs I’m assigning an ID to the child element with a counter that adds 1 every time a new input is generated. But when I try to remove inputs by their ID’s, only the latest created input seems to be removable.

I’ve noticed that if you generate say 5 new inputs, and then click the remove button on say input:2, then it removes the 5th input and button. So even though I’m setting the input and button ID’s earlier on in the process, clicking the remove button is relying on the row counter’s current value only.

Could anyone give me some pointers as to how to whether old IDs can be pointed to or if I would need to rewrite this in a totally different way? I’m very new to JS so apologies if this is horrendous JS scripting!

var row = 1;

function addFields() {
  row++;

  var container = document.getElementById("container");

  var input = document.createElement("input");
  input.id = "input_" + row
  input.type = "text";
  input.placeholder = "input:" + row;
  container.appendChild(input);

  var button = document.createElement("button");
  button.id = "button_" + row;
  button.type = "button";
  button.innerText = "Remove";
  button.onclick = function() {
    remove_field("button_" + row, "input_" + row);
  }
  container.appendChild(button);
}

function remove_field(button_id, input_id) {
  var elem = document.getElementById(button_id);
  elem.remove();
  var elem = document.getElementById(input_id);
  elem.remove();
}
<input type="text" id="goal" name="goal">
<button type="button" onclick="addFields()">+</button><br>
<div class="container" id="container" />

3

Answers


  1. The value of row updates each time you add a new element, so don’t directly use row to get the id. Directly access button.id and input.id instead.

    var row = 1;
    
    function addFields() {
      row++;
    
      var container = document.getElementById("container");
    
      var input = document.createElement("input");
      input.id = "input_" + row
      input.type = "text";
      input.placeholder = "input:" + row;
      container.appendChild(input);
    
      var button = document.createElement("button");
      button.id = "button_" + row;
      button.type = "button";
      button.innerText = "Remove";
      button.onclick = function() {
        remove_field(button.id, input.id);
      }
      container.appendChild(button);
    }
    
    function remove_field(button_id, input_id) {
      var elem = document.getElementById(button_id);
      elem.remove();
      var elem = document.getElementById(input_id);
      elem.remove();
    }
    <input type="text" id="goal" name="goal">
    <button type="button" onclick="addFields()">+</button><br>
    <div class="container" id="container" />

    Alternatively, get rid of the remove_field function entirely and just call remove on the variables input and button.

    var row = 1;
    
    function addFields() {
      row++;
    
      var container = document.getElementById("container");
    
      var input = document.createElement("input");
      input.id = "input_" + row
      input.type = "text";
      input.placeholder = "input:" + row;
      container.appendChild(input);
    
      var button = document.createElement("button");
      button.id = "button_" + row;
      button.type = "button";
      button.innerText = "Remove";
      button.onclick = function() {
        button.remove();
        input.remove();
      }
      container.appendChild(button);
    }
    <input type="text" id="goal" name="goal">
    <button type="button" onclick="addFields()">+</button><br>
    <div class="container" id="container" />
    Login or Signup to reply.
  2. The value of row changes on each iteration and your click function merely regards the last value it had. You can either scope it using let, bind it or just pass the elements to your remove function, which also skips you the the lookups.

    //REM: Can remove the row with this solution, just left to show you the issue.
    var row = 1;
    
    function addFields() {
      row++;
    
      var container = document.getElementById("container");
    
      var input = document.createElement("input");
      input.id = "input_" + row
      input.type = "text";
      input.placeholder = "input:" + row;
      container.appendChild(input);
    
      var button = document.createElement("button");
      button.id = "button_" + row;
      button.type = "button";
      button.innerText = "Remove";
      
      //REM: Binding the elements
      //REM: Alternatively you can bind the current value of row.
      button.onclick = function(buttonElement, inputElement, currentRow){
        console.log('This is the value row would have: ', row);
        console.log('This is the value row had at time of binding: ', currentRow);
        
        //REM: Passing the elements to skip any lookup
        remove_field(buttonElement, inputElement)
      }.bind(button, button, input, row);
      
      container.appendChild(button);
    }
    
    //REM: Removes a button-input pair
    function remove_field(buttonElement, inputElement){
      buttonElement?.remove();
      inputElement?.remove()
    }
    <input type="text" id="goal" name="goal">
    <button type="button" onclick="addFields()">+</button><br>
    <div class="container" id="container" />
    Login or Signup to reply.
  3. The other answers explained the issue.

    I strongly suggest you do not keep a counter.

    Here is a delegation version using a div container per row and recommended eventListeners

    window.addEventListener('DOMContentLoaded', () => {
      const container = document.getElementById('container');
      const renum = () => {
        container.querySelectorAll('input[type=text]').forEach((inp,i) => {
          inp.id = "input_" + (i+1);
          inp.placeholder = "input:" + (i+1);
        });  
      };
      container.addEventListener('click', (e) => {
        const tgt = e.target;
        if (tgt.matches('.remove')) {
          tgt.closest('div').remove();
          renum();
        }  
      });  
      document.getElementById('add').addEventListener('click', () => {
        const div = document.createElement('div');
        const input = document.createElement('input');
        input.type = "text";
        div.appendChild(input);
        const button = document.createElement("button");
        button.classList.add("remove");
        button.type = "button";
        button.innerText = "Remove";
        div.appendChild(button);
        container.appendChild(div);
        renum();    
      });
    });
    <input type="text" id="goal" name="goal">
    <button type="button" id="add">+</button><br>
    <div class="container" id="container"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search