e.iluf
e.iluf

Reputation: 1659

How do I add event listener to dynamic element Id?

I am using firebase DB to dynamically generate some html using the function below.

function Inventorytable(data) {
  var container = document.getElementById('iteminfo'); 
  container.innerHTML = '';

  data.forEach(function(InventSnap) { // loop over all jobs
    var key = InventSnap.key;
    var Items = InventSnap.val();
    var jobCard = `
      <td class="text-left" id="${key}"><a href>${key}</a href></td>
      <td class="text-left" >${Items.PartNumber}</td>
    `;
    container.innerHTML += jobCard;
  })
}

I want to add an event listener to the first td class with id="${key}". I know with a normal id I can use document.getElementById("xxxx").addEventListener but since this is dynamic and the id is the firebase key. How do I add an event listener using the element id?

Upvotes: 0

Views: 5920

Answers (5)

Jack jdeoel
Jack jdeoel

Reputation: 4584

Create a function to call when all append data is finished !! I tested with add() ,also for more easy to handle ,add one distinct class name to td !!!

var data = [
{key:0,val:"Zero"},
{key:1,val:"One"}
]
Inventorytable(data);
function Inventorytable(data) {
  var container = document.getElementById('iteminfo'); 
  container.innerHTML = '';
  data.forEach(function(InventSnap) { // loop over all jobs
    var key = InventSnap.key;
    var Items = InventSnap.val;
    var jobCard = `
      <td class="text-left first" id="${key}"><a href="#!">${key}</a href></td>
      <td class="text-left" >${Items}</td>
    `;
    container.innerHTML += jobCard;
  });
  add();
}

function add() {
    var container = document.querySelectorAll(".first");
    [].map.call(container, function(elem) {
    elem.addEventListener("click",function(){
      console.log(this.id);
    }, false);
});
}
<table>
<tr id="iteminfo"></tr>
</table>

Upvotes: 1

Rory McCrossan
Rory McCrossan

Reputation: 337560

You could change your logic to create the td elements and add the event handler to them at that point. This avoids the issue of deleting the existing HTML within the container, along with all the existing event handlers. Try this:

data.forEach(function(InventSnap) { // loop over all jobs
  var key = InventSnap.key;
  var Items = InventSnap.val();

  var td0 = document.createElement('td');
  td0.id = key;
  td0.classList.add('text-left');
  container.appendChild(td0);

  td0.addEventListener('click', function() {
    console.log('clicked!');
  });

  var a = document.createElement('a');
  a.href = '#';
  a.innerText = key;
  td0.appendChild(a);

  var td1 = document.createElement('td');
  td1.innerText = Items.PartNumber;
  td1.classList.add('text-left');
  container.appendChild(td1);
});

Or the equivalent of this in jQuery would be:

data.forEach(function(inventSnap) {
  var key = inventSnap.key;
  var items = inventSnap.val();

  var $td = $(`<td class="text-left" id="${key}"><a href="#">${key}</a></td>`).on('click', function() {
    console.log('clicked!');
  }).appendTo(container);

  container.append(`<td class="text-left">${items.PartNumber}</td>`);      
});

Upvotes: 2

Quentin
Quentin

Reputation: 943214

Normally: Exactly the same way.

You have the ID in a variable. You can get the element. document.getElementById(key).

… but there's a problem.

container.innerHTML += jobCard;

You keep taking the DOM (with any element listeners bound to it), converting it to HTML (which doesn't carry the event listeners across), appending to it, then converting the HTML back to DOM (giving you a new set of elements without the event listeners).


Rather than destroying the elements each time, you should create them using standard DOM. You can then call addEventListener after creating the element.

data.forEach(function(InventSnap) { // loop over all jobs
  var key = InventSnap.key;
  var Items = InventSnap.val();

  var td = document.createElement("td");
  td.setAttribute("class", "text-left");
  td.setAttribute("id", key);
  td.addEventListener("click", your_event_listener_function);

  var a = document.createElement("a");
  a.setAttribute("href", "");
  a.appendChild(document.createTextNode(key));

  td.appendChild(a);
  container.appendChild(td);

  td = document.createElement("td");
  td.setAttribute("class", "text-left");
  td.appendChild(document.createTextNode(Items.PartNumber));

  container.appendChild(td);

})

You could (either with your original approach or in combination with the above) use delegated events instead:

container.addEventListener("click", delegated_event_listener);

function delegated_event_listener(event) {
    if (test_what_element_was_clicked_on(this)) {
        do_something_with(this.id);
    }
}

Upvotes: 1

lumio
lumio

Reputation: 7575

I would suggest adding a click handler to the #iteminfo as otherwise you would have stall event listeners around when you reorganize your table.

var container = document.getElementById('iteminfo');
container.addEventListener( 'click', ( e ) => {
  const id = e.target.id;
  console.log( id );
} );

var data = [
  { key: 1 },
  { key: 2 },
  { key: 3 },
  { key: 'some-other-key' },
]

function Inventorytable(data) {
  container.innerHTML = '';

  data.forEach(function(InventSnap) { // loop over all jobs
    var key = InventSnap.key;
    var jobCard = `<button id="${key}">${ key }</button>`;

    container.innerHTML += jobCard;
  })
}
Inventorytable( data );
<div id="iteminfo"></div>

Upvotes: 1

Abbas
Abbas

Reputation: 14432

You can use event delegation for this:

document.addEventListener('click',function(e) {
    if(e.target && e.target.id === "yourID") {
        //do something
    }
});

Upvotes: 0

Related Questions