Jay Corbett
Jay Corbett

Reputation: 28441

Building an HTML table on the fly using jQuery

Below is the code I use to build an HTML table on the fly (using JSON data received from the server).

I display an animated pleasewait (.gif) graphic while the data is loading. However, the graphic freezes while the JavaScript function is building the table. At first, I was just happy to make this happen (display the table), I guess now I need to work on efficiency. At the very least I need to stop the animated graphic from freezing. I can go to a static "Loading" display, but I would rather make this method work.

Suggestions for my pleasewait display? And efficiency? Possibly a better way to build the table? Or maybe not a table, but some other "table" like display

var t = eval( "(" + request + ")" ) ;
var myTable = '' ;
myTable += '<table id="myTable" cellspacing=0 cellpadding=2 border=1>' ;
myTable +=  "<thead>" ;
myTable +=   "<tr>";
for (var i = 0; i < t.hdrs.length; i++) {
    myTable +=    "<th>"     + header +       "</th>";
}
myTable +=   "</tr>" ;
myTable +=  "</thead>" ;
myTable +=  "<tbody>" ;

for (var i = 0; i < t.data.length; i++) {
    myTable +=    '<tr>';
    for (var j = 0; j < t.hdrs.length; j++) {
        myTable += '<td>';
        if (t.data[i][t.hdrs[j]] == "") {
            myTable += "&nbsp;" ;
        }
        else {
            myTable += t.data[i][t.hdrs[j]] ;
        }
        myTable += "</td>";
    }
    myTable +=    "</tr>";
}
myTable +=  "</tbody>" ;
myTable += "</table>" ;

$("#result").append(myTable) ;
$("#PleaseWaitGraphic").addClass("hide");
$(".rslt").removeClass("hide") ;

Upvotes: 21

Views: 63792

Answers (8)

Andrew
Andrew

Reputation:

Search the web for JavaScript and StringBuilder. Once you have a JavaScript string builder make sure you use the .append method for every concatenation. That is, you don't want to have any + concatenations. After that, search for JavaScript and replacehtml. Use this function instead of innerHTML.

Upvotes: 0

bhollis
bhollis

Reputation: 4683

Using innerHTML can definitely be much faster than using jQuery's HTML-to-DOM-ifier, which uses innerHTML but does a lot of processing on the inputs.

I'd suggest checking out chain.js as a way to quickly build out tables and other repeating data structures from JavaScript objects. It's a really lightweight, smart databinding plugin for jQuery.

Upvotes: 3

Andrew Hedges
Andrew Hedges

Reputation: 21796

You basically want to set up your loops so they yield to other threads every so often. Here is some example code from this article on the topic of running CPU intensive operations without freezing your UI:

function doSomething (progressFn [, additional arguments]) {
    // Initialize a few things here...
    (function () {
        // Do a little bit of work here...
        if (continuation condition) {
            // Inform the application of the progress
            progressFn(value, total);
            // Process next chunk
            setTimeout(arguments.callee, 0);
        }
    })();
}

As far as simplifying the production of HTML in your script, if you're using jQuery, you might give my Simple Templates plug-in a try. It tidies up the process by cutting down drastically on the number of concatenations you have to do. It performs pretty well, too after I recently did some refactoring that resulted in a pretty big speed increase. Here's an example (without doing all of the work for you!):

var t = eval('(' + request + ')') ;
var templates = {
    tr : '<tr>#{row}</tr>',
    th : '<th>#{header}</th>',
    td : '<td>#{cell}</td>'
};
var table = '<table><thead><tr>';
$.each(t.hdrs, function (key, val) {
    table += $.tmpl(templates.th, {header: val});
});
...

Upvotes: 26

Guichard
Guichard

Reputation: 171

I've been using JTemplates to accomplish what you are describing. Dave Ward has an example on his blog here. The main benefit of JTemplates is that your html isn't woven into your javascript. You write a template and call two functions to have jTemplate build the html from your template and your json.

Upvotes: 11

Chase Seibert
Chase Seibert

Reputation: 15861

My experience has been that there are two discrete delays. One is concatenating all those strings together. The other is when the browser actually tries to render the string. Typically, it's IE that has the most trouble with UI freezes, in part because it's a lot slower at running javascript. This should get better in IE8.

What I would suggest in your case is breaking the operation into steps. Say for a 100 row table, you produce a valid 10 row table first. Then you output that to screen and use a setTimeout to return control to the browser so the UI stops blocking. When the setTimeout comes back, you do the next 10 rows, etc.

Creating the table using DOM is certainly "cleaner", as others have said. However, there is a steep price to pay in terms of performance. See the excellent quirksmode article on this subject, which has some benchmarks you can run yourself.

Long story short, innerHTML is much, much faster than DOM, even on modern JS engines.

Upvotes: 0

Lasar
Lasar

Reputation: 5437

You could insert the table into the DOM bit by bit. Honestly I'm not entirely sure if this will help with your problem, but it's worth a try. I'd do it roughly like this (untested code, could be refine some more):

$("#result").append('<table id="myTable" cellspacing=0 cellpadding=2 border=1></table>');
$('#myTable').append('<thead><tr></tr></thead>');
$('#myTable').append('<tbody></tbody>');

for (var i = 0; i < t.hdrs.length; i++) { 
    $('#myTable thead tr').append('<th>'+header+'</th>');
}

for (var i = 0; i < t.data.length; i++) { 
 myTr =    '<tr>';
 for (var j = 0; j < t.hdrs.length; j++) { 
  myTr += '<td>';
  if (t.data[i][t.hdrs[j]] == "") { 
   myTr += "&nbsp;" ; 
  }
  else { 
   myTr += t.data[i][t.hdrs[j]] ; 
  }
  myTr += "</td>";
  }
 myTr +=    "</tr>";
 $('#myTable tbody').append(myTr);
}

$("#PleaseWaitGraphic").addClass("hide");
$(".rslt").removeClass("hide") ;

Upvotes: -2

neouser99
neouser99

Reputation: 1827

For starters, check out flydom and it's variants, they will help termendously. Could you possibly give more context? If this is not in the onload and just pasted in the page, just wrapping the whole thing in $(function () { /* code */ }) will probably clean up everything you are having problems with. Inline JS is executed immediately, which means that loop for the table. onload is an event and essentially 'detached'.

Upvotes: 0

Jim
Jim

Reputation: 73966

What you are doing is building a string, and then parsing it all at once upon insertion. What about creating an actual table element (i.e. $("<table>")), and then adding each row to it in turn? By the time you actually insert it into the page, the DOM nodes will all have been constructed, so it shouldn't be as big a hit.

Upvotes: 3

Related Questions