Add unique ID's to input fields created with createElement method

140 views Asked by At

I'm trying to create a dynamic membership form, where I can send the ID of input fields as a shortcode into an email. The form generally begins with 2 fields (1 for name, 1 for email). I can then dynamically add new fields or remove with 2 control buttons.

I have managed to achieve adding and removing fields, but the issue I have is the ID of the new name field is showing "email_2", while the ID of the new email field is not being added.

I also need help removing 2 fields at once with the removeField link. It appears to only be removing one field at at time.

Here's my code...

// set variables
let survey = document.querySelector(".survey");
let surveyInputs = document.querySelectorAll(".survey_field");
let addField = document.getElementById("add_field");
let removeField = document.getElementById("remove_field");

// increment id's with counter
let nameId = "name_";
let emailId = "email_";
let nameCounter = 1;
let emailCounter = 1;

// Add new text field and email field
addField.addEventListener("click", () => {
  let newNameField = document.createElement("input");
  newNameField.setAttribute("type", "text");
  newNameField.setAttribute("name", "survey_options");
  newNameField.setAttribute("class", "survey_field");
  newNameField.setAttribute("id", nameId + (++nameCounter));
  newNameField.setAttribute("placeholder", "Another Name");
  survey.appendChild(newNameField);
  let newEmailField = document.createElement("input");
  newEmailField.setAttribute("type", "email");
  newEmailField.setAttribute("name", "survey_options");
  newEmailField.setAttribute("class", "survey_field");
  newNameField.setAttribute("id", emailId + (++emailCounter));
  newEmailField.setAttribute("placeholder", "Another Email");
  survey.appendChild(newEmailField);
});

// Remove extra text field and email field at once
removeField.addEventListener("click", () => {
  if (survey.children.length > 2) {
    survey.removeChild(survey.lastChild); // this removes only 1 field, how can I remove 2 at once?
  }
});
.wrapper {
  width: 38vw;
  margin: 3em auto;
  padding: 1em;
  border-radius: 6px;
  background-color: white;
  box-shadow: 10px 10px 10px 0 rgba(47, 47, 47, 0.2);
}

input[name="survey_options"] {
  margin: 10px;
  padding: 10px;
  border-radius: 6px;
  border: solid 1px #e2e2e2;
  background: none;
  width: 15em;
  color: black;
}

input[name="survey_options"]:focus {
  outline: solid 1px lightgrey;
}

.controls {
  width: 25vw;
  margin: 1em auto;
}

a {
  text-decoration: none;
  color: black;
  font-weight: 700;
}

.svg-inline--fa {
  margin-right: 5px;
}

#remove_field {
  float: right;
}
<div class="wrapper">
  <div class="survey">
    <input type="text" name="survey_options" class="survey_field" id="name_1" placeholder="Name" data-name="1" />
    <input type="email" name="survey_options" class="survey_field" id="email_1" placeholder="Email" data-email="1" />
  </div>
  <div class="controls">
    <a href="#" id="add_field"><i class="fa fa-plus"></i>Add Field</a>
    <a href="#" id="remove_field"><i class="fa fa-window-close"></i>Remove Field</a>
  </div>
</div>

3

There are 3 answers

4
Basil jose On

In the add field event listener variable name was wrong

// set variables
 let survey = document.querySelector(".survey");
 let surveyInputs = document.querySelectorAll(".survey_field");
 let addField = document.getElementById("add_field");
 let removeField = document.getElementById("remove_field");
 
 // increment id's with counter
 let nameId = "name_";
 let emailId = "email_";
 let nameCounter = 1;
 let emailCounter = 1;
 
 // Add new text field and email field
 addField.addEventListener("click", () => {
   let newNameField = document.createElement("input");
   newNameField.setAttribute("type", "text");
   newNameField.setAttribute("name", "survey_options");
   newNameField.setAttribute("class", "survey_field");
   newNameField.setAttribute("id", nameId + (++nameCounter));
   newNameField.setAttribute("placeholder", "Another Name");
   survey.appendChild(newNameField);
 
   let newEmailField = document.createElement("input");
   newEmailField.setAttribute("type", "email");
   newEmailField.setAttribute("name", "survey_options");
   newEmailField.setAttribute("class", "survey_field");
   newEmailField.setAttribute("id", emailId + (++emailCounter));
   newEmailField.setAttribute("placeholder", "Another Email");
   survey.appendChild(newEmailField);
 
 });
 
 // Remove extra text field and email field at once
 removeField.addEventListener("click", () => {
   if (survey.children.length > 2) {
     let last_id = survey.lastChild.id.split("_")[1];
     if (last_id) {
       let nameFieldToRemove = document.getElementById(`name_${last_id}`)
       survey.removeChild(survey.lastChild); // this removes only 1 field, how can I remove 2 at once?
       survey.removeChild(nameFieldToRemove);
     }
   }
 });

Remove both added fields function also updated you can either use removeChild twice or use the above method to find the element and remove it

7
mplungjan On

You had a typo, but to avoid all that, just wrap the fields and clone

// set variables
let survey = document.querySelector(".survey");
let addField = document.getElementById("add_field");
let removeField = document.getElementById("remove_field");

const renumber = () => {
  [...survey.children]
  .forEach((div, i) => div.querySelectorAll('input')
    .forEach(inp => {
      // renumber the IDs and data-name
      const index = i + 1;
      inp.id = `${inp.name}_${index}`;
      inp.dataset[inp.placeholder.toLowerCase()] = index;
    })
  )
};
// Add new text field and email field
addField.addEventListener("click", () => {
  const div = survey.lastElementChild.cloneNode(true);
  div.querySelectorAll('input').forEach(inp => inp.value = ""); // clear the value from the cloned fields
  survey.appendChild(div);
  renumber();
});

// Remove extra text field and email field at once
removeField.addEventListener("click", () => {
  if (survey.children.length > 1) {
    survey.removeChild(survey.lastChild); // this removes the div
    renumber();
  }
});
.wrapper {
  width: 38vw;
  margin: 3em auto;
  padding: 1em;
  border-radius: 6px;
  background-color: white;
  box-shadow: 10px 10px 10px 0 rgba(47, 47, 47, 0.2);
}

input[name="survey_options"] {
  margin: 10px;
  padding: 10px;
  border-radius: 6px;
  border: solid 1px #e2e2e2;
  background: none;
  width: 15em;
  color: black;
}

input[name="survey_options"]:focus {
  outline: solid 1px lightgrey;
}

.controls {
  width: 25vw;
  margin: 1em auto;
}

a {
  text-decoration: none;
  color: black;
  font-weight: 700;
}

.svg-inline--fa {
  margin-right: 5px;
}

#remove_field {
  float: right;
}
<div class="wrapper">
  <div class="survey">
    <div class="item">
      <input type="text" name="survey_options" class="survey_field" placeholder="Name" data-name="1" />
      <input type="email" name="survey_options" class="survey_field" placeholder="Email" data-email="1" />
    </div>
  </div>
  <div class="controls">
    <a href="#" id="add_field"><i class="fa fa-plus"></i>Add Field</a>
    <a href="#" id="remove_field"><i class="fa fa-window-close"></i>Remove Field</a>
  </div>
</div>

0
ladetunji On

Thanks, @mplungjan and @basil-jose for your solutions. It really opened my mind to problem-solving. I ran the question through Bard AI and then iterated a few times to get this.

// Get the HTML elements
const survey = document.querySelector(".survey");
const addField = document.getElementById("add_field");
const removeField = document.getElementById("remove_field");

addField.addEventListener("click", () => {
  // Get the current count of text and email input fields
  const nameCount = document.querySelectorAll("input[type=text]").length;
  const emailCount = document.querySelectorAll("input[type=email]").length;

  // Create a new text input element
  const nameField = document.createElement("input");
  nameField.type = "text";
  nameField.name = "survey_options";
  nameField.setAttribute("class", "survey_field");
  nameField.placeholder = "New Name";
  nameField.id = `name_${nameCount + 1}`;

  // Create a new email input element
  const emailField = document.createElement("input");
  emailField.type = "email";
  emailField.name = "survey_options";
  emailField.setAttribute("class", "survey_field");
  emailField.placeholder = "New Email";
  emailField.id = `email_${emailCount + 1}`;

  // Append the text and email input elements to the button element
  survey.appendChild(nameField);
  survey.appendChild(emailField);
});

removeField.addEventListener("click", () => {
  // Get the last two input fields in the document
  const lastEmailField = survey.lastElementChild;
  const lastNameField = lastEmailField.previousElementSibling;

  // Remove the input fields from the document
  if (survey.children.length > 2) {
    lastNameField.remove();
    lastEmailField.remove();
  }
});

The nameCount and emailCount variables were gamechangers, as it solved the issue I noticed in @basil-jose's solution where whenever I removed a row of fields, the inputCount never reduced and only kept increasing after subsequent clicks - meaning I could have 3 rows, but the inputs in memory are probably 12.

The sibling method also helped with picking inputs either side of the lastChild, making the removal process simple too.

Thanks again.