1

I'm trying to build a page form in HTML with a button that allows you to add and remove fields dynamically. In particular, I want on click 3 new field.

I follow indications on W3C and StackOverflow but my code doesn't work. In added in Safari with developer instruments I don't get any kind of error.

This is my HTML code:

var numWriters;

function isNumberKey(event) {
  var code = event.which || event.keyCode;
  if (code > 31 && (code < 48 || code > 57))
    return false;
  return true;
}

function resetNumWriters() {
  numWriters = 1;
}

function addWriter() {
  numWriters++;

  var divWriters = document.getElementById("containerWriters");
  var ul = document.createElement("ul");
  var liName = document.createElement("li");

  var labelName = document.createElement("label");
  var strongName = document.createElement("strong");
  strongName.InnerHTML = "Writer's name";
  var inputTextName = document.createElement("input");
  inputTextName.setAttribute("type", "text");
  inputTextName.setAttribute("name", "writerName" + numWriters);

  divWriters.appendChild(ul);
  ul.appendChild(liName);
  liName.appendChild(labelName);
  labelName.appendChild(strongName);
  liName.appendChild(inputTextName);

  var liSurname = document.createElement("li");
  var labelSurname = document.createElement("label");
  var strongSurname = document.createElement("strong");
  strongSurname.InnerHTML = "Writer's surname";
  var inputTextSurname = document.createElement("input");
  inputTextSurname.setAttribute("type", "text");
  inputTextSurname.setAttribute("name", "writerSurname" + numWriters);

  ul.appendChild(liSurname);
  liSurname.appendChild(labelSurname);
  labelSurname.appendChild(strongSurname);
  liSurname.appendChild(inputTextSurname);

  var liDateOfBirth = document.createElement("li");
  var labelDateOfBirth = document.createElement("label");
  var strongDateOfBirth = document.createElement("strong");
  strongDateOfBirth.InnerHTML = "Writer's dateOfBirth";
  var inputDateOfBirth = document.createElement("input");
  inputDateOfBirth.setAttribute("type", "date");
  inputDateOfBirth.setAttribute("name", "writerDateOfBirth" + numWriters);

  ul.appendChild(liDateOfBirth);
  liSurname.appendChild(labelDateOfBirth);
  labelDateOfBirth.appendChild(strongDateOfBirth);
  liDateOfBirth.appendChild(inputDateOfBirth);
}
<form class="formSpaceDataProduct" action="addBook" method="POST">
  <fieldset>
    <legend> Insert the book data </legend>
    <ul>
      <li>
        <label> <strong>Title:</strong> </label>
        <input type="text" name="Title" placeholder="Title of book" maxlength="50" required></input>
      </li>
      <li>
        <label> <strong>Quantity:</strong> </label>
        <input type="text" name="Quantity" placeholder="Max quantity in stock can be 999" maxlength="3" pattern="[0-9]{1,3}" onkeydown="return isNumberKey(event)" required></input>
      </li>
      <li>
        <label> <strong>Year of publication:</strong> </label>
        <input type="text" name="yearOfPublication" placeholder="Year of publication" maxlength="4" pattern="[0-9]{1,4}" onkeydown="return isNumberKey(event)" required></input>
      </li>
      <li>
        <label> <strong>Genre:</strong> </label>
        <select name="Genre">
          <option value="Select">Select</option>
          <option value="History">History</option>
          <option value="Fantasy">Fantasy</option>
          <option value="Child">Child</option>
          <option value="Art">Art</option>
          <option value="Music">Music</option>
          <option value="Thriller">Thriller</option>
          <option value="Travel">Travel</option>
          <option value="Biography">Biography</option>
          <option value="Poetry">Poetry</option>
          <option value="Romance">Romance</option>
          <option value="Science">Science</option>
        </select>
      </li>
      <li>
        <label> <strong>Number of pages:</strong> </label>
        <input type="text" name="numPages" placeholder="Max length: 99.999 pages" maxlength="5" pattern="[0-9]{1,5}" onkeydown="return isNumberKey(event)" required></input>
      </li>
      <li>
        <label> <strong>ISBN:</strong> </label>
        <input type="text" name="ISBN" placeholder="A 13-digit numric code" maxlength="13" pattern="[0-9]{13}" onkeydown="return isNumberKey(event)" required></input>
      </li>
      <li>
        <label> <strong>Publisher:</strong> </label>
        <input type="text" name="publisher" placeholder="The name of the publishing house" maxlength="30" required></input>
      </li>
      <li>
        <label> <strong>Writer's name:</strong> </label>
        <input type="text" name="writerName1" placeholder="The name of the Writer" maxlength="20" required></input>
      </li>
      <li>
        <label> <strong>Writer's surname:</strong> </label>
        <input type="text" name="writerSurname1" placeholder="The surname of the Writer" maxlength="20" required></input>
      </li>
      <li>
        <label> <strong>Writer's date of birth:</strong> </label>
        <input type="date" name="writerDateOfBirth1" required></input>
      </li>
    </ul>

    <div id="containerWriters">
      <button type="button" id="addWriter" onclick="addWriter"> Add Writer </button>
    </div>

    <!--Submit all data -->
    <input type="submit" value="Insert" id="submit"></input>
  </fieldset>
</form>

In my JS code the variable numWriters would like to be used to count how many writers are already added at the form. This because I need the attribute "name" of each field of each writer different from previous. I want reset this variable in each page that include this javascript file. Is it correct?

Thank a lot.

3
  • your last line should be divWriters.appendChild(ul); instead of first. Commented Aug 28, 2019 at 18:09
  • Instead of adding a number to the end of the name of the copied elements you can just do name="writer[name][]" (and so on) then you can iterate over the arrays after the form is posted (first writer is 0, second is 1 etc...). Then your button can just copy the whole block Commented Aug 28, 2019 at 18:17
  • You also never set the value (or call the function to do so); for var numWriters; Commented Aug 28, 2019 at 18:20

1 Answer 1

1

First of all Welcome to Stackoverflow! :)

regarding your code:

  • in your button tag, to call a function, the onclick event must specify a function and all you're specifying is a string. you should change onclick="addWriter" into onclick="addWriter()"

  • because of the name of your function, please change the button id into something else, for example <button type="button" id="btnAddWriter" ...

  • you also need to understand that the way of appending elements is very important, if you create a label, and a strong and then set the innerHTML of your strong tag, but append the label first, the strong will have no text...

  • you're also forgetting to set your global variable, so I will just assume that you call resetNumWriters() on page load...

  • for last, and to avoid DRY (Don't Repeat Yourself) I would suggest a code like:

<script>
    var numWriters;

    function isNumberKey(event) {
      var code = event.which || event.keyCode;
      if (code > 31 && (code < 48 || code > 57))
        return false;
      return true;
    }

    function resetNumWriters() {
      numWriters = 1;
    }

    function addTextField(title, numWriters) {
      console.log('addTextField ' + numWriters);
      var liName = document.createElement("li");
      var labelName = document.createElement("label");
      var strongName = document.createElement("strong");
      strongName.innerHTML = title + ":&nbsp;";
      var inputTextName = document.createElement("input");
      inputTextName.setAttribute("type", "text");
      inputTextName.setAttribute("name", "writerName" + numWriters);

      labelName.appendChild(strongName);
      liName.appendChild(labelName);
      liName.appendChild(inputTextName);

      return liName;
    }

    function addBirthdayField(title, numWriters) {
      console.log('addBirthdayField ' + numWriters);
      var liDateOfBirth = document.createElement("li");
      var labelDateOfBirth = document.createElement("label");
      var strongDateOfBirth = document.createElement("strong");
      strongDateOfBirth.innerHTML = title + ":&nbsp;";
      var inputDateOfBirth = document.createElement("input");
      inputDateOfBirth.setAttribute("type", "date");
      inputDateOfBirth.setAttribute("name", "writerDateOfBirth" + numWriters);

      labelDateOfBirth.appendChild(strongDateOfBirth);
      liDateOfBirth.appendChild(labelDateOfBirth);
      liDateOfBirth.appendChild(inputDateOfBirth);

      return liDateOfBirth;
    }

    function addWriter() {
      numWriters++;
      console.log('adding Writter ' + numWriters);

      var divWriters = document.getElementById("containerWriters");
      var ul = document.createElement("ul");

      var labelName = addTextField("Writer's name", numWriters);
      var labelSurname = addTextField("Writer's surname", numWriters);
      var labelBirthday = addBirthdayField("Writer's date of birth", numWriters);

      ul.appendChild(labelName);
      ul.appendChild(labelSurname);
      ul.appendChild(labelBirthday);

      divWriters.appendChild(ul);
    }

    resetNumWriters();
</script>

edited August 29th, 2019 - added remove writer code

  • creating a button and styling it in <style> that will remove the ul
  • extrated addLabelField to it's own function
<style>
    fieldset { width: 480px; }
    ul button { position: relative; float: right; }
</style>

<script>
    var numWriters;

    function isNumberKey(event) {
      var code = event.which || event.keyCode;
      if (code > 31 && (code < 48 || code > 57))
        return false;
      return true;
    }

    function resetNumWriters() {
      numWriters = 1;
    }

    // added function
    function addLabelField(title) { 
      var label = document.createElement("label");
      var strong = document.createElement("strong");
      strong.innerHTML = title + ":&nbsp;";

      label.appendChild(strong);
      return label;
    }

    function addTextField(title, numWriters) {
      console.log('addTextField ' + numWriters);

      var liName = document.createElement("li");
      var labelName = addLabelField(title);

      var inputTextName = document.createElement("input");
      inputTextName.setAttribute("type", "text");
      inputTextName.setAttribute("name", "writerName" + numWriters);

      liName.appendChild(labelName);
      liName.appendChild(inputTextName);

      return liName;
    }

    function addBirthdayField(title, numWriters) {
      console.log('addBirthdayField ' + numWriters);

      var liDateOfBirth = document.createElement("li");
      var labelDateOfBirth = addLabelField(title);

      var inputDateOfBirth = document.createElement("input");
      inputDateOfBirth.setAttribute("type", "date");
      inputDateOfBirth.setAttribute("name", "writerDateOfBirth" + numWriters);

      liDateOfBirth.appendChild(labelDateOfBirth);
      liDateOfBirth.appendChild(inputDateOfBirth);

      return liDateOfBirth;
    }

    function addWriter() {
      numWriters++;
      console.log('adding Writter ' + numWriters);

      var divWriters = document.getElementById("containerWriters");

      // added button
      var btn = document.createElement("button");
      btn.innerHTML = "x";
      btn.setAttribute("onclick", "removeWriter("+ numWriters + ")");
      btn.setAttribute("title", "remove this writer");

      var ul = document.createElement("ul");
      ul.setAttribute("data-writter-id", numWriters);

      var labelName = addTextField("Writer's name", numWriters);
      var labelSurname = addTextField("Writer's surname", numWriters);
      var labelBirthday = addBirthdayField("Writer's date of birth", numWriters);

      ul.appendChild(btn);
      ul.appendChild(labelName);
      ul.appendChild(labelSurname);
      ul.appendChild(labelBirthday);

      divWriters.appendChild(ul);
    }

    function removeWriter(id) {
        document.querySelectorAll('ul[data-writter-id="' + id + '"]')[0].remove();
    }

    resetNumWriters();
</script>

You can find a full example on Fiddle - https://jsfiddle.net/balexandre/5k9rhyxo/3

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lot. This is a great answer. The last question: you call resetNumWriters() at the bottom of the js file. This mean that every page HTML which "include" or recall the js file in <head> reset the global variable in the js file?
@DavideNetti the issue is that you start with var numWriters; that only defines the variable name but will have undefined as value, and your first call is numWriters++; that will throw a NaN (Not A Number) as you can't add 1 to an undefined value... calling resetNumWriters(); at the bottom is the same as you started with var numWriters = 1;, remember that code will only run once (on page refresh) and in your code, you never reset the variable again...
@DavideNetti added code to remove each writer as well full example on Fiddle (just press "Run" on that page)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.