Search code examples
javascripthtmlarraysdomdom-events

How to make a delete button to delete an item, to which it belongs, from the list?


Sorry for my English.I have an array of items, that are being rendered on my web-page. Each item has a delete button, but how can I anchor a click on a button to the item, that it(button) belongs to? I mean: how can I delete exactly that item, which button I clicked? Do I need to add IDs to my items, if so, how can I do that? Preferably without server or React, just JS Looking forward for your help!

From my CodePen

HTML

<section class="main">
  <div class="container">
    <h1 class="mb-3">Details storage</h2>
    <form class="mb-3" action="" id="add-detail">
      <div class="mb-3">
        <input required class='form-control' type="number" placeholder="Detail Code" id="input-key">
        </div>
      <div class="mb-3">
        <input required class='form-control' type="text" placeholder="Category" id="input-category">
        </div>
      <div class="mb-3">
        <input required class='form-control' type="number" placeholder="Amount" id="input-amount">
      </div>
      <div class="mb-3">
        <input required class='form-control' type="number" placeholder="Price per piece" id="input-price">
        </div>
      <button class='btn btn-primary'>Add</button>
    </form>
    <table class="table table-striped detailsTable">
      <thead>
      <th data-column="key">Detail Code</th>
      <th data-column="category">Category</th>
      <th data-column="amount">Amount</th>
      <th data-column="price">Price per piece</th>
      <th data-column="totalPrice">Total amount</th> 
      <th></th>
      </thead>
      <tbody id="details-list">
      </tbody>
    </table>
  </div> 
</section>

JS

class Detail {
  constructor(key, category, amount, price) {
    this.key=key
    this.category=category
    this.amount=amount
    this.price=price
  }
  get totalPrice() {
    return (this.amount * this.price);
}
}

const details=[
  new Detail(0,'Nails',300,4),
  new Detail(1,'Screws',400,6),
  new Detail(2,'Bolts',600,5),
  new Detail(3,'Self-tapping screw',700,3),
  new Detail(4,'Nails', 400, 2)
]

const $detailsList = document.getElementById('details-list'),
      $detailsListTHALL = document.querySelectorAll('.detailsTable th')

let column = 'key',
    columnDir = true

function newDetailTR(detail){
  const $detailTR= document.createElement('tr'),
        $keyTD= document.createElement('td'),
        $categoryTD= document.createElement('td'),
        $amountTD= document.createElement('td'),
        $priceTD= document.createElement('td'),
        $totalPriceTD=document.createElement('td'),
        $deleteTD=document.createElement('button')
  
  $keyTD.textContent=detail.key
  $categoryTD.textContent=detail.category
  $amountTD.textContent=detail.amount
  $priceTD.textContent=detail.price
  $totalPriceTD.textContent = detail.totalPrice
  $deleteTD.textContent="Delete"
  
  $detailTR.append($keyTD)
  $detailTR.append($categoryTD)
  $detailTR.append($amountTD)
  $detailTR.append($priceTD)
  $detailTR.append($totalPriceTD)
  $detailTR.append($deleteTD)
  
  return $detailTR;
}


function getSortDetails(prop, dir) {
  const detailsCopy=[...details]
  return detailsCopy.sort(function(detailA, detailB){
    if((!dir==false? detailA[prop] <detailB[prop] : detailA[prop] > detailB[prop])) return -1;
  })
}

function render(){
  let detailsCopy=[...details];
  
  detailsCopy=getSortDetails(column, columnDir);
  
  $detailsList.innerHTML = '';
  for(const detail of detailsCopy){
    $detailsList.append(newDetailTR(detail))
  }
}


$detailsListTHALL.forEach(elment => {
  elment.addEventListener('click', function() {
    column = this.dataset.column;
    columnDir = !columnDir;
    render()
  })
})


document.getElementById('add-detail').addEventListener('submit', function(event){
  event.preventDefault()
  details.push(new Detail(
    Number(document.getElementById('input-key').value),
    document.getElementById('input-category').value,
    Number(document.getElementById('input-amount').value),
    Number(document.getElementById('input-price').value)
  ))
  
  render()
  alert('Item has been added!')
  event.preventDefault();
  event.target.reset(); 
})

render()

Solution

  • When you create your TR element, assign a click to it; basically like this (by using a reusable function elNew to create new DOM elements):

    const $detailTR = elNew('tr')
    const $deleteButton = elNew('button', {
      textContent: "Delete",
      onclick() {
        removeDetail(detail); // Remove from array
        $detailTR.remove();   // Remove from HTML
      }
    });
    

    also, don't forget to:

    • create a TD wrapper for your delete button
    • don't clone your arrays, otherwise you'll not be able to keep track of the item index to delete (after you sort the array)

    Example:

    // DOM utility functions:
    const el = (sel, par = document) => par.querySelector(sel);
    const els = (sel, par = document) => par.querySelectorAll(sel);
    const elNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
    
    class Detail {
      constructor(key, category, amount, price) {
        this.key = key;
        this.category = category;
        this.amount = amount;
        this.price = price;
      }
      get totalPrice() {
        return (this.amount * this.price);
      }
    }
    
    const details = [
      new Detail(0, 'Nails', 300, 4),
      new Detail(1, 'Screws', 400, 6),
      new Detail(2, 'Bolts', 600, 5),
      new Detail(3, 'Self-tapping screw', 700, 3),
      new Detail(4, 'Wheels', 400, 2),
    ];
    
    const $detailsList = el('#details-list');
    const $detailsListTHALL = els('.detailsTable th');
    
    let column = 'key';
    let columnDir = true;
    
    function removeDetail(detail) {
      const index = details.indexOf(detail);
      if (index > -1) details.splice(index, 1);
    }
    
    function newDetailTR(detail) {
      const $detailTR = elNew('tr'),
            $keyTD = elNew('td', { textContent: detail.key }),
            $categoryTD = elNew('td', { textContent: detail.category }),
            $amountTD = elNew('td', { textContent: detail.amount }),
            $priceTD = elNew('td', { textContent: detail.price }),
            $totalPriceTD = elNew('td', { textContent: detail.totalPrice }),
            $deleteTD = elNew('td'),
            $deleteButton = elNew('button', {
              textContent: "Delete",
              onclick() {
                removeDetail(detail); // Remove from array
                $detailTR.remove();   // Remove from HTML
              }
            });
      $deleteTD.append($deleteButton);
      $detailTR.append($keyTD, $categoryTD, $amountTD, $priceTD, $totalPriceTD, $deleteTD);
      return $detailTR;
    }
    
    function getSortDetails(prop, dir) {
      return details.sort(function(a, b) {
        if((!dir==false? a[prop] < b[prop] : a[prop] > b[prop])) return -1;
      });
    }
    
    function render() {
      const detailsCopy = getSortDetails(column, columnDir);
      $detailsList.innerHTML = "";
      details.forEach(detail => $detailsList.append(newDetailTR(detail)));
    }
    
    el('#add-detail').addEventListener('submit', function(event){
      event.preventDefault();
    
      details.push(new Detail(
        el('#input-key').valueAsNumber,
        el('#input-category').value,
        el('#input-amount').valueAsNumber,
        el('#input-price').valueAsNumber
      ));
    
      render();
      event.target.reset(); 
    })
    
    render();
      <section class="main">
      <div class="container">
        <h1 class="mb-3">Details storage</h2>
        <form class="mb-3" action="" id="add-detail">
          <div class="mb-3">
            <input required class='form-control' type="number" placeholder="Detail Code" id="input-key">
            </div>
          <div class="mb-3">
            <input required class='form-control' type="text" placeholder="Category" id="input-category">
            </div>
          <div class="mb-3">
            <input required class='form-control' type="number" placeholder="Amount" id="input-amount">
          </div>
          <div class="mb-3">
            <input required class='form-control' type="number" placeholder="Price per piece" id="input-price">
            </div>
          <button class='btn btn-primary'>Add</button>
        </form>
        <table class="table table-striped detailsTable">
          <thead>
            <th data-column="key">Detail Code</th>
            <th data-column="category">Category</th>
            <th data-column="amount">Amount</th>
            <th data-column="price">Price per piece</th>
            <th data-column="totalPrice">Total amount</th> 
            <th></th>
          </thead>
          <tbody id="details-list"></tbody>
        </table>
      </div> 
    </section>