0

I create dinamically a table of emails, and I would like to give to each row of this table an event listener so that I can click on each email. This is my code,

for(var i=0;i<emails.length;i++){
      let row = document.createElement('tr');
      row.addEventListener('click', () => email(emails[i].id));
      let cols = '<td>' + emails[i].recipients + '</td><td>' + emails[i].subject + '</td><td style="width:35%;"> ' + body + '</td><td>'+emails[i].timestamp+'</td>';
      row.innerHTML = cols;
      document.getElementById('table-body').appendChild(row);
}

Where emails is a Nodelist.

This is the table:

enter image description here

I'm receiving this error:

Uncaught TypeError: Cannot read property 'id' of undefined
    at HTMLTableRowElement.<anonymous> (inbox.js:56)

How can I solve this? Thank you!

sunhearth
  • 93
  • 1
  • 9
  • If you only want to have the e-mail clicked, you can wrap the content inside the column with the `a` tag. – Sanguinary Sep 06 '20 at 17:06
  • I need to use the email function and to pass the id of the parameter, how can I make this with the a tag? @Sanguinary – sunhearth Sep 06 '20 at 17:07
  • "So that I can click on each email". What exactly are you looking for functionality-wise in this statement? Are you looking for the `mailto:` link functionality, or what are you trying to do? If it's the `mailto`, you can simply add that with you dynamically created elements. No need for JavaScript. In case you're looking for further functionality, is jQuery an option, or do you have to stick with vanilla JS? – Martin Sep 06 '20 at 17:09
  • Can you show how does emails array look like? – Nikhil Singh Sep 06 '20 at 17:10

1 Answers1

1

A simple solution with ES6 - use let instead of var when defining the loop index variable.

This question has a lot of details about the why you're getting the undefined error as well as some alternative solutions. It boils down to the way that variables are scoped in javascript - var is scoped to the function, not the block as it is in a C-style language.

The below is a working example, though I did locally define some dummy data structures and swap out the email function for a console.log.

let body = "some email body";
let emails = [];
emails.push({id: "1", timestamp: "Some time", recipients: "person1, person2", subject: "Subject 1"});
emails.push({id: "2", timestamp: "Some time", recipients: "person2", subject: "Subject 2"});


for(let i = 0; i < emails.length; i++){
      let row = document.createElement('tr');
      row.addEventListener('click', function() {console.log(emails[i].id);});
      let cols = '<td>' + emails[i].recipients + '</td><td>' + emails[i].subject + '</td><td style="width:35%;"> ' + body + '</td><td>'+emails[i].timestamp+'</td>';
      row.innerHTML = cols;
      document.getElementById('table-body').appendChild(row);
}
<body>
  <table>
    <tbody id="table-body"></tbody>
  </table>
<body>
patrickb
  • 422
  • 2
  • 9
  • I just changed var in let for the loop index variable and it works, thank you so much! Can you explain me why it works with let and not with var? – sunhearth Sep 06 '20 at 19:25
  • When you bind the variable with `var`, you're using a variable that continues to change due to incrementing the loop counter. The important thing with `var` is that there is a single copy of `i`, and each callback is referencing the same, changing, variable. After the loop, `i` will have a value of `2`, which is why you were getting the `undefined` error, no matter which row you clicked on - every function was trying to find `emails[emails.length+1]`! When you use `let`, each iteration through the loop gets a new copy of the variable, `i`, and you are bound to that new (unchanging) variable. – patrickb Sep 06 '20 at 20:11