4
\$\begingroup\$

This function is meant to take an object, loop through the key-values and generate/build a HTML table at a particular ID:

function insertBasicTable(basic_data, insert_id) {
  /* Insert the key/value pairs into a table element on the DOM.

  basic_data = object containing key/value pairs
  insert_id  = the id on the DOM to insert the table into 

  Intentionally not including closing tags in the jquery requests;
  http://stackoverflow.com/a/14737115/2355035 */

  table_id = '#' + insert_id;
  var array_length = Object.keys(basic_data).length;
  var array_obj_names = Object.keys(basic_data);

  // create the table header
  $(table_id).empty();
  $(table_id).append('<thead>');
  $(table_id).find('thead:last').append('<th>Tag');
  $(table_id).find('thead:last').append('<th>Data');

  // begin the table body and iterate through key/value pairs
  $(table_id).append('<tbody>');
  for (var i = 0; i < array_length; i++) {
    var attr_name = array_obj_names[i];
    var tag       = '<td>' + array_obj_names[i];
    var data      = '<td>' + basic_data[attr_name];

    $(table_id).find('tbody:last').append('<tr>' + tag + data);
  }
}

This function is then used, along with a number of D3 scripts, to populate elements on the DOM:

linePlot.render(someObject.modules.qual.quintiles);
boxPlot.render(someObject.modules.qual.quintiles);
insertBasicTable(someObject.modules.basic, 'basic-stats-table');
// etc, etc...

Any comments about how this is being achieved? Am I overlooking either a basic tenant of how JS "should" be used (this is a huge side-effect for instance, should I be returning a formatted string then placing that string into the DOM for instance) or over-complicating things by jQuery'ing a bunch of times?

\$\endgroup\$
6
\$\begingroup\$

Let's start with your version:

1) Unnecessary optimization.

var array_length = Object.keys(basic_data).length;
var array_obj_names = Object.keys(basic_data);

This gives you nothing in terms of performance. It would be optimized away by the browser.

2) Chain-calling

$(table_id).empty();
$(table_id).append('<thead>');

It is possible to do chain-calls like: $('#elem').doA().doB().doC()

3) Misunderstanding calls

$(table_id).find('thead:last').append('<th>Tag');

.append() does exactly that: appending after the last element.

4) NuStyleLoopz

  for (var i = 0; i < array_length; i++) {
    var attr_name = array_obj_names[i];
    var tag       = '<td>' + array_obj_names[i];
    var data      = '<td>' + basic_data[attr_name];

You could iterate over the keys directly via Object.keys() with

Here a slighty pimped version:

makeTag=function(openTag, closeTag){
            return function(content){
                return openTag+content+closeTag;  
            };
        };
tHead=makeTag("<thead>","</thead>");
tBody=makeTag("<tbody>","</tbody>");
td=makeTag("<td>","</td>");
tr=makeTag("<tr>","</tr>");

function insertBasicTable(data,id){
    $('#'+id).html(
        tHead(
            tr(
                td("Tag")+
                td("Data")
            )
        )+
        tBody(
            Object.keys(data).reduce(function(o,n){
                return o+tr(
                    td(n)+""+
                    td(data[n]+"")
                );
            },"")
        )
    );
};

insertBasicTable({"key1":"value1","key2":"value2"},"mytable");

Here is the fiddle to play with.

To make HTML-generation less painfull and a bit more JS-stylish I define what is called a higher order function makeTag: simply a function returning a function. A call to var tHead=makeTag("<thead>","</thead>"); generates an anonymous function (a function without name), which returns every time it's called "<thead>"+ parameter +"</thead>". The same goes for the other tags. This allows me to do something like a DSL:

    tHead(
        tr(
            td("Tag")+
            td("Data")
        )
    )

That is not only handy, but cool B-) Besides it is nearly as readable as html and not so noisy like the typical string concatenation and the risk of typos is really low.

\$\endgroup\$
1
\$\begingroup\$

You should cache $table and $tbody, instead of re-instantiating them every time you need them.

\$\endgroup\$
1
\$\begingroup\$

Well, you've avoided the biggest potential issue by separating your data composition from your markup creation and manipulation.

Your situation could be improved by changing your method to a "factory" method that is responsible for creating a table and then returning it.

Then, it would be up to the caller of the table creation method to do what it needs to with the table. Whether the table is returned as a string, DOM element, or jQuery object is up to the needs of the consuming code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.