-1

In my case. i want to create an onclick event that will be do multiple work.

Here is an image

in this image has an Add Item button. it will be increased the below html when click on it.

var addItemHTML = '<tr class="hold-item">\
           <td><strong>'+typeField.value+': </strong></td>\
           <td><input type="text"></td>\
           <td><img class="cancel-item" src="icon/cancel.png" height="25px"></td>\
           </tr>';

So far so good. but here, i want to remove increased html one by one as my choose by click on each close icon. but i can't di that!

i'm trying with this below javascript.

  var cancelIcon = document.getElementsByClassName("cancel-icon");
  var holeElement = document.getElementsByClassName("hole-element");
 
  for(i=0;i < holeIElement.length;i++){
   cancelIcon[i].onclick = function(){
     holeElement[i].remove();
   }
   
 }

it doesn’t work. if anyone solve me!

mplungjan
  • 169,008
  • 28
  • 173
  • 236

2 Answers2

1

Delegate!

Example

NOTE: I added tbody and use addEventListener for the onclick too

var addItemBtn = document.getElementById("add-item-btn");
var itemTable = document.getElementById("item-table");
var typeField = document.getElementById("type-field");

addItemBtn.addEventListener("click", function() {

  var addItemHTML = '<tr class="hole-element">\
               <td><strong>' + typeField.value + ': </strong></td>\
               <td><input type="text"></td>\
               <td><img class="cancel-icon" src="icon/cancel.png" height="25px"></td>\
               </tr>';

  itemTable.innerHTML += addItemHTML;
})

document.getElementById("item-table").addEventListener("click", function(e) {
  const tgt = e.target;
  if (tgt.classList.contains("cancel-icon")) tgt.closest("tr").remove();
});
.product_add {
  overflow: hidden;
}

.product_add_left_side {
  /* background: rgb(230,230,230);*/
  display: inline-block;
  float: left;
  padding: 10px;
}

.product_add_right_side {
  /*background: rgb(200,200,200);*/
  display: inline-block;
  float: left;
  padding: 10px;
}

.cancel-icon {
  transition: 0.3s;
  margin-left: 15px;
}

.cancel-icon:active {
  -webkit-transform: scale(0.9);
}
<div class="product_add">

  <div class="product_add_left_side">
    <img src="icon/image.jpg" height="220px">
  </div>

  <div class="product_add_right_side">
    <table>
      <tbody id="item-table">
        <tr>
          <td><strong>Name: </strong></td>
          <td><input type="text"></td>
        </tr>

        <tr>
          <td><strong>Unit: </strong></td>
          <td><input type="text"></td>
        </tr>

        <tr>
          <td><strong>Price: </strong></td>
          <td><input type="text"></td>
        </tr>

        <tr>
          <td><strong>In Stock: </strong></td>
          <td><input type="text"></td>
        </tr>

        <tr class="hole-element">
          <td><strong>Hello: </strong></td>
          <td><input type="text"></td>
          <td><img class="cancel-icon" src="icon/cancel.png" height="25px"></td>
        </tr>

        <tr class="hole-element">
          <td><strong>Hello: </strong></td>
          <td><input type="text"></td>
          <td><img class="cancel-icon" src="icon/cancel.png" height="25px"></td>
        </tr>
      </tbody>
    </table>

    <input type="text" id="type-field">
    <button id="add-item-btn">Add Item</button>
    <div id="demo"></div>
  </div>

</div>

<div id="load-product-js"></div>

Otherwise you need to have a closure

JavaScript closure inside loops – simple practical example

Also you have cancel-icon vs cancel-item

mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • May be, i set up it as not good. it make change as removing the hole table when change the "this.closest("tr").remove()" to "this.remove()". but nothing change with the closest("tr") as removing by single each item. – Anas Habib Nov 11 '20 at 10:55
  • `tgt`, not `this`. Sorry: `if (tgt.classList.contains("cancel-icon")) tgt.closest("tr").remove();` – mplungjan Nov 11 '20 at 11:10
0

Preferably, you should use document.createElement() to create new elements, and either ParentNode.append() or Node.appendChild() for adding that element to the DOM.
The advantages of using them are:

  • No (better: less) risk of invalidating your HTML
  • No chance of accidentally removing listener of existing elements
  • Easier configuration of the newly created element(-s) (like adding listener)

One downside of using them is, that it makes your JavaScript quite a bit more lengthy.

However, using said method of adding the new <tr> allowed me to easily reference it for removal upon pressing the cancel-button.

That means, with easy referencing, it is easy to dynamically add rows with the behavior you wanted.

Sidenote
I have replaced <td><strong></strong></td> with <th></th> since the apparent purpose of the nested elements were exactly what <th> is semantically about.

I have also added the click-listener to a button, which contains the cancel-icon, since adding a click-listener to something different than <a>, <button> or <input> (meaning: any element which is not inherently clickable) is more difficult to set up to be accessible. You can still stylize the button to be without border and background, so that only the image is visible as you wanted.

const itemTable = document.querySelector("#item-table");
const typeField = document.querySelector("#type-field");
document.querySelector("#add-item-btn").addEventListener("click", () => {
  let tr = document.createElement("tr");
  tr.classList.add("hole-element");
  
  let th = document.createElement("th");
  th.innerHTML = typeField.value + ":";
  th.setAttribute("style", "text-align: left");
  tr.append(th);
  
  let td = document.createElement("td");
  tr.append(td);
  
  let input = document.createElement("input");
  input.setAttribute("type", "text");
  td.append(input);
  
  td = document.createElement("td");
  tr.append(td);
  
  let button = document.createElement("button");
  button.classList.add("cancel-icon");
  button.addEventListener("click", () => {
    tr.remove();
  });
  td.append(button);
  
  let img = document.createElement("img");
  img.setAttribute("src", "icon/cancel.png");
  img.setAttribute("height", "25px");
  button.append(img);
  
  itemTable.append(tr);
  typeField.value = "";
});
th {text-align: left}
.cancel-icon{
  transition: 0.3s;
  margin-left: 15px;
}
.cancel-icon:active{transform: scale(0.9)}
<table id="item-table">
  <tr>
    <th>Name: </th>
    <td><input type="text"></td>
  </tr>
  <tr>
    <th>Unit: </th>
    <td><input type="text"></td>
  </tr>
  <tr>
    <th>Price: </th>
    <td><input type="text"></td>
  </tr>
  <tr>
    <th>In Stock: </th>
    <td><input type="text"></td>
  </tr>
</table>

<input type="text" id="type-field">
<button id="add-item-btn">Add Item</button>
Oskar Grosser
  • 2,804
  • 1
  • 7
  • 18
  • 1
    I still think delegation is more elegant – mplungjan Nov 11 '20 at 11:59
  • 1
    Yes, I also think that delegation as how you showed it is better, more clean, and uses less objects. I just wanted to show a different way of achieving the same behavior. – Oskar Grosser Nov 11 '20 at 12:07
  • You mean, I should use `closest("tr")`? That would work as long as the button is not re-located to somewhere else in the DOM, resulting in unpredictable behavior. I like it more to bind it directly to the `` which it originally belonged to. But I think that's where tastes differ. – Oskar Grosser Nov 11 '20 at 12:11
  • wow! it’s wonderful prescript! i got another way also. Thanks a lot man! Thanks a lot! – Anas Habib Nov 11 '20 at 13:20