views:

10442

answers:

8

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 js 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") ;

Thanks (please add a comment to the question for clarification requests and/or code bugs)

+1  A: 

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.

Jim
A: 

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'.

neouser99
A: 

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") ;
_Lasar
A: 

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.

Chase Seibert
Nobody has suggested creating the table using the DOM, so your reference isn't applicable. Inserting content with innerHTML creates DOM nodes. Referring to this fact is not the same thing as suggesting the use of createElement.
Jim
+6  A: 

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.

Guichard
Definately agree here, client site templating is the best way in which you can create HTML elements from a data source.
Slace
+9  A: 

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});
});
...
Andrew Hedges
+1  A: 

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.

BRH
A: 

Search the web for javascript and stringbuilder. once you have a javascript string builder make sure you use the .append method for every concat. i.e. you don't want to have any + concatinations. after that search for javascript and replacehtml. use this function instead of innerHTML.