2

I would like to build dynamic table using JQuery and then append the table to DOM. Usually I used string concatenation method to build the table. This time I would like to do this with JQuery. One thing that I'm struggling to get is opening/closing tr element while looping over multiple rows. Here is example if my code:

var data = {
  "1": {
    "fname": "Jon",
    "lname": "Wayne",
    "dob": "05/14/1987",
    "status": "Active",
    "color": "Red",
    "team": "Miami"
  },
  "2": {
    "fname": "Tim",
    "lname": "Ryan",
    "dob": "01/23/1967",
    "status": "Inactive",
    "color": "Blue",
    "team": "Chicago"
  },
  "3": {
    "fname": "Jim",
    "lname": "Carey",
    "dob": "11/02/1997",
    "status": "Suspended",
    "color": "Yellow",
    "team": "Denver"
  },
  "4": {
    "fname": "Chuck",
    "lname": "Norris",
    "dob": "09/06/1945",
    "status": "Active",
    "color": "Green",
    "team": "Boston"
  }
}

$('#start').on('click', showRecords);
function showRecords() {
  displayData(1,data);
}

function displayData(fnid,data) {
  var tbl = $('<table>').addClass('display').prop('id','data_tbl'),
   thead = $('<thead>'),
   tbody = $('<tbody>'),
   tr = $('<tr>');
   title = ['First Name','Last Name','DOB','Status','Color','Team'];

  /*** Start: Table Header ***/
  thead.append('<tr>');
  for(var i=0; i < title.length; i++) {
   if(fnid == 1){
    if(i <= 3) {
     thead.append($('<th>').text(title[i]));
    }
   }else{
    thead.append($('<th>').text(title[i]));
   }
  }
  thead.append('</tr>');
  /*** End: Table Header ***/

  /*** Start: Table Body ***/
  for(key in data) {
   tbody.append('<tr>');
   tbody.append($('<td>').text(data[key].fname));
   tbody.append($('<td>').text(data[key].lname));
   tbody.append($('<td>').text(data[key].dob));
   tbody.append($('<td>').text(data[key].status));
   if(fnid !== 1) {
    tbody.append($('<td>').text(data[key].color));
    tbody.append($('<td>').text(data[key].team));
   }
   tbody.append('</tr>');
  }
  /*** End: Table Body ***/

  tbl.append(thead); // Append header section to table.
  tbl.append(tbody); // Append body section to table.
  
    $("#container").empty().append(tbl);
 }
.display {
  width: 500px;
  background-color: red;
}
.display,
.display th,
.display td{
  border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="button" name="start" id="start" value="Start" />
<div id="container"></div>

At first look you would think that everything is fine with the code, but if you open your dev tools and inspect the table elements you will find the issues. There are tr elements in thead and tbody that are empty. These elements should not be there. Can anyone tell me how I can open/close the tr element in JQuery and build the table properly? Thank you.

espresso_coffee
  • 5,980
  • 11
  • 83
  • 193
  • You need to create tr append it to thead or tbody and td and th append to tr not to tbody. – jcubic Apr 24 '19 at 15:07
  • You don't need to close anything. Just append the elements. (You don't need to construct an HTML string.) –  Apr 24 '19 at 15:08
  • Yea @ChrisG is right when you create $('') you are creating DOM element like in inspector so you need to add that element to tr not to tbody, you're not working with string here but same objects like in inspector. – jcubic Apr 24 '19 at 15:10
  • Your issue is you are confusing html with DOM Element creation. HTML uses open and close tags to denote the start and stop of an element, because that's all it has. But when you create elements with javascript, that's not an issue. It's up to you to add the needed children to the elements. – Taplar Apr 24 '19 at 15:10
  • I would really appreciate if some can provide an example. Thank you. – espresso_coffee Apr 24 '19 at 15:13
  • Also as a side note; if you find yourself doing things like this a lot, I would encourage you to look into importing in some form of a templating engine such as handlebars or mustache. Using templates allows you to extract the html out of your javascript, clean up your logic, and further the separation of concerns practice. – Taplar Apr 24 '19 at 15:13
  • @Taplar Do you have a small sample code that you can share? Thank you. – espresso_coffee Apr 24 '19 at 15:14
  • 2
    Here's the code, fixed: https://jsfiddle.net/khrismuc/t2rq47gk/ –  Apr 24 '19 at 15:15

3 Answers3

2

Use Object.keys to loop over your object.

var data = {
  "1": {
    "fname": "Jon",
    "lname": "Wayne",
    "dob": "05/14/1987",
    "status": "Active",
    "color": "Red",
    "team": "Miami"
  },
  "2": {
    "fname": "Tim",
    "lname": "Ryan",
    "dob": "01/23/1967",
    "status": "Inactive",
    "color": "Blue",
    "team": "Chicago"
  },
  "3": {
    "fname": "Jim",
    "lname": "Carey",
    "dob": "11/02/1997",
    "status": "Suspended",
    "color": "Yellow",
    "team": "Denver"
  },
  "4": {
    "fname": "Chuck",
    "lname": "Norris",
    "dob": "09/06/1945",
    "status": "Active",
    "color": "Green",
    "team": "Boston"
  }
}

$('#start').on('click', showRecords);
function showRecords() {
  displayData(1,data);
}

function displayData() {
  const table = $("<table></table>").addClass('display');

  Object.keys(data).forEach(item => {
    const row = $("<tr></tr>");
    Object.keys(data[item]).forEach(key => {
      const rowData = $("<td></td>")
        .addClass("bar")
        .text(data[item][key]);
      row.append(rowData);
    });
    table.append(row);
  });

  $("#container").empty().append(table);
}
.display {
  width: 500px;
  background-color: red;
}
.display,
.display th,
.display td{
  border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="button" name="start" id="start" value="Start" />
<div id="container"></div>
Junius L
  • 15,881
  • 6
  • 52
  • 96
1

I would suggest extracting your html out into a template. That way your script is cleaned up and you separate your markup from your logic more so. The following logic uses the created template and then only has to construct the cells for the tbody. It constructs the cells in an array of string using map, which then all the strings get appended to the tbody at once.

var data = {
  "1": {
    "fname": "Jon",
    "lname": "Wayne",
    "dob": "05/14/1987",
    "status": "Active",
    "color": "Red",
    "team": "Miami"
  },
  "2": {
    "fname": "Tim",
    "lname": "Ryan",
    "dob": "01/23/1967",
    "status": "Inactive",
    "color": "Blue",
    "team": "Chicago"
  },
  "3": {
    "fname": "Jim",
    "lname": "Carey",
    "dob": "11/02/1997",
    "status": "Suspended",
    "color": "Yellow",
    "team": "Denver"
  },
  "4": {
    "fname": "Chuck",
    "lname": "Norris",
    "dob": "09/06/1945",
    "status": "Active",
    "color": "Green",
    "team": "Boston"
  }
}

$('#start').on('click', showRecords);

function showRecords() {
  displayData(1,data);
}

function displayData(fnid,data) {
  var template = $('#tableTemplate').html();
  var $table = $(template);
  
  $table.find('tbody').append(
    Object.keys(data[fnid]).map(function(key){
      return '<td>'+ data[fnid][key] +'</td>';
    })
  );
 
  $("#container").empty().append($table);
}
.display {
  width: 500px;
  background-color: red;
}
.display,
.display th,
.display td{
  border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="button" name="start" id="start" value="Start" />
<div id="container"></div>

<script type="text/html" id="tableTemplate">
  <table class="display" id="data_tbl">
    <thead>
      <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>DOB</th>
        <th>Status</th>
        <th>Color</th>
        <th>Team</th>
      </tr>
    </thead>
    <tbody>
    </tbody>
  </table>
</script>
Taplar
  • 24,788
  • 4
  • 22
  • 35
  • I understand that code would be cleaner if I separate my html code. In the other hand now days `React`, `Vue`, `etc` are implementing Virtual DOM and all HTML code is built in JavaScript. I'm just thinking about efficiency int his case as well even this example only have 4 records. – espresso_coffee Apr 24 '19 at 15:37
  • @espresso_coffee If you are building an application from scratch, React, Vue, and the like could be contenders. However, if you have an existing application, it may be more worth while to do templates like this or with handlebars or mustache, rather than pulling in an entire framework and having to rewrite your application. – Taplar Apr 24 '19 at 15:39
  • I agree. In this case I'm working on old application. The only thing that I use is JQuery library. I'm still trying to implement efficient code that is easy for the maintenance at the same time. I know that building html with JavaScript is not considered the best practice in this example. – espresso_coffee Apr 24 '19 at 15:50
-2

Object.keys(data).forEach(function(item, key){ data[item].fname });