Using javascript to display user input with table and deleting a with button

I am struggling to get this working and would like to know if i’m headed in the right direction with what i have.

Whats supposed to be happening is two input fields that get added to a on a click event. This part is done and its working fine.

Whats supposed to happen after the input fields get added to the table is you can then delete a tr with a button that gets added when the tr is created. Which is not working. I also cant figure out why the page refreshes when you click the delete button to delete a tr, preventDefault() is being used.

let data             = [];
let speciesInput     = document.querySelector('#speciesInput')
let breedInput       = document.querySelector('#breedInput')
let messageBox       = document.getElementById("display");
const addProductBtn  = document.querySelector('#addValue')
let deleteProductBtn = document.getElementsByClassName('deleteProduct')
const deleteBtnArray = Array.from(deleteProductBtn)

addProductBtn.addEventListener('click', function (e) {
    e.preventDefault();

    let species, breed;
    species = speciesInput.value;
    breed   = breedInput.value;

    data.push({
        species: species,
        breed: breed,
    });
    clearAndShow();
})  

function clearAndShow() {
    // Clear our fields
    speciesInput.value   = "";
    breedInput.value     = "";
    messageBox.innerHTML = computeHTML();
}

function computeHTML() {
    let html = "<table>";
    data.forEach(function (item) {
        html += "<tr>";
        html += "<td>" + item.species + '&nbsp' + "</td>" 
        html += "<td>" + item.breed + "</td>"
        html += "<td>" + "<button class='deleteProduct' onclick='deleteProduct()'> &#8722; </button>" + "</td>"
        html += "</tr>"
        console.log(deleteBtnArray)
    });
    html += "</table>"
    return html;
}

function deleteProduct() {
    console.log(deleteBtnArray)
    deleteBtnArray.forEach((btn) => {
        btn.addEventListener('click', function (e) {
            e.preventDefault();
            console.log('Delete item')
            deleteItem() // Not made yet
        })
    })
}
deleteProduct()



document.querySelector('#productForm').addEventListener('submit', function (e) {
    e.preventDefault();
})
<!-- Add Product -->
<div class="">
  <div class="d-flex flex-column justify-content-center align-content-center">
    <div class="intro-wrapper">
      <h1 class="welcome-heading">Welcome <span class="brand-heading"></span><span class="brand-heading brand-heading-grey"></span></h1>
      <span class="intro-description">Set up your profile to begin interacting with the community.</span>
    </div>
    <span class="secondary-txt mt-5 mb-2">Add Your Product For Display</span>
    <form id="productForm" class="form d-flex flex-column align-items-center mt-2" action="">
      <div class="product-field-outter">
        <div class="form-field product-field-inner d-flex flex-column align-items-center">
          <div style="width: 100%!important;" class="form-field my-auto">
            <label for="species"></label>
            <input id="speciesInput" class="sign-up-inputs addProductInputs" type="text" placeholder="Species">
          </div>
          <div style="width: 100%!important;" class="form-field my-auto">
            <label for="breed"></label>
            <input id="breedInput" class="sign-up-inputs addProductInputs" type="text" placeholder="Breed">
          </div>
          <small></small>
          <button id="addValue" class="add-product-btn btn-active mt-4 ms-2">&#43;</button>
        </div>
      </div>
      <div id="display" class="output d-flex flex-column justify-content-center mt-5"></div>
      <!-- <ul id="list" class="productList justify-content-between"></ul> -->
      <button class="primary-btn continue-btn btn-active mt-4">Continue</button>
    </form>
    <button class="back-btn btn-active me-auto mt-4">Back</button>
  </div>
</div>
</div>
<!-- Add Product -->
<div class="">
    <div class="d-flex flex-column justify-content-center align-content-center">
        <div class="intro-wrapper">
            <h1 class="welcome-heading">Welcome<span class="brand-heading"></span><span class="brand-heading brand-heading-grey"></span></h1>
            <span class="intro-description">Set up your profile to begin interacting with the community.</span>
        </div>
        <span class="secondary-txt mt-5 mb-2">Add Your Product For Display</span>
        <form class="form d-flex flex-column align-items-center mt-2" action="">
            <div class="product-field-outter">
                <div class="form-field product-field-inner d-flex flex-column align-items-center">
                    <div style="width: 100%!important;" class="form-field my-auto">
                        <label for="species"></label>
                        <input id="speciesInput" class="sign-up-inputs addProductInputs" type="text" placeholder="Species">
                    </div>
                    <div style="width: 100%!important;" class="form-field my-auto">
                        <label for="breed"></label>
                        <input id="breedInput" class="sign-up-inputs addProductInputs" type="text" placeholder="Breed">
                    </div>
                    <small></small>
                    <button id="addValue" class="add-product-btn btn-active mt-4 ms-2">&#43;</button>
                </div>
            </div>
            <div id="display" class="output d-flex flex-column justify-content-center mt-5"></div>
            <!-- <ul id="list" class="productList justify-content-between"></ul> -->
            <button class="primary-btn continue-btn btn-active mt-4">Continue</button>
        </form>
        <button class="back-btn btn-active me-auto mt-4">Back</button>
        </div>
    </div>
</div>

Javascript

let data = [];
let speciesInput = document.querySelector('#speciesInput')
let breedInput = document.querySelector('#breedInput')

let messageBox = document.getElementById("display");
const addProductBtn = document.querySelector('#addValue')
let deleteProductBtn = document.getElementsByClassName('deleteProduct')
const deleteBtnArray = Array.from(deleteProductBtn)

addProductBtn.addEventListener('click', function (e) {
    e.preventDefault();

    let species, breed;
    species = speciesInput.value;
    breed = breedInput.value;

    data.push({
        species: species,
        breed: breed,
    });
    clearAndShow();
})  

function clearAndShow() {
    // Clear our fields
    speciesInput.value = "";
    breedInput.value = "";
    messageBox.innerHTML = computeHTML();
}

function computeHTML() {
    let html = "<table>";
    data.forEach(function (item) {
        html += "<tr>";
        html += "<td>" + item.species + '&nbsp' + "</td>" 
        html += "<td>" + item.breed + "</td>"
        html += "<td>" + "<button class='deleteProduct' onclick='deleteProduct()'> &#8722; </button>" + "</td>"
        html += "</tr>"
        console.log(deleteBtnArray)
    });
    html += "</table>"
    return html;
}

function deleteProduct() {
    console.log(deleteBtnArray)
    deleteBtnArray.forEach((btn) => {
        btn.addEventListener('click', function (e) {
            e.preventDefault();
            console.log('Delete item')
            deleteItem() // Not made yet
        })
    })
}
deleteProduct()

Answer

There is more than one thing happening

  1. The variables on top of your JS code are recovering elements from the document object only when the page loads. You are not updating them (I.E. the deleteBtnArray) after appending the html on clearAndShow() function. That’s why the deleteBtnArray is always empty when you click the – buttons. One way to avoid this is collectiong the elements inside the deleteProduct function. Also, you don’t need to convert into array using Array.from because document.getElementsByClassName(...) already returns an iterable collection. Also, using “for of” is a little better readable than forEach with callbacks.

  2. You are adding the deleteProduct(...) function more than once using this method, because you are calling using the onclick='deleteProduct()' and also btn.addEventListener('click'...). And after that you also call the deleteProduct function right after its declaration. No need for that.

  3. If you want to remove the whole <tr>, there is a easy way of doing that referencing each <tr> with an id and using it as argument for the deleteProduct function, just like in the example below

Replace your whole javascript by this, and use the same HTML

let data = [];
var speciesInput = document.querySelector('#speciesInput')
var breedInput = document.querySelector('#breedInput')
var messageBox = document.getElementById("display");
var addProductBtn = document.querySelector('#addValue')

addProductBtn.addEventListener('click', function (e) {
  e.preventDefault();

  let species, breed;
  species = speciesInput.value;
  breed = breedInput.value;

  data.push({
    species: species,
    breed: breed,
  });
  clearAndShow();
})

function clearAndShow() {
  // Clear our fields
  speciesInput.value = "";
  breedInput.value = "";
  messageBox.innerHTML = computeHTML();
}

function computeHTML() {
  let html = "<table>";
  data.forEach(function (item, index) {
    html += "<tr id='line-" + index + "'>";
    html += "<td>" + item.species + '&nbsp' + "</td>"
    html += "<td>" + item.breed + "</td>"
    html += "<td>" + "<button onclick='deleteProduct(" + index + ")'> &#8722; </button>" + "</td>"
    html += "</tr>"
  });
  html += "</table>"
  return html;
}

function deleteProduct(removedIndex) {
  document.getElementById("line-" + removedIndex).remove()
  data = data.filter(function (item, index) { return index != removedIndex })
}

document.querySelector('#productForm').addEventListener('submit', function (e) {
  e.preventDefault();
})

Bonus:

I recommend you to use ` instead of ” or ‘ when writing html inside strings, because

  • You can insert variables using ${variableName}
  • You can break lines
  • You can use ‘ and ” freely inside strings delimited by `
  • You don’t need to use a lot of +=

I also recommend you to use .map( instead of .forEach(. Things get cleaner this way.

Replace the computeHTML() I’ve shown before with this piece of code:

function itemHtml(item, index) {
  return `<tr id="line-${index}">
    <td>${item.species}</td>
    <td>${item.breed}</td>
    <td><button onclick="deleteProduct(${index})"> &#8722; </button></td>
  </tr>`
}

function computeHTML() {
  return `<table>${data.map(itemHtml)}</table>`
}