3

I have a table with 100 columns. I'm running a loop through all the th elements, to explicitly set the width of each th.

var $ths = $("table th");
$ths.each(function (i, th) {
    var $th = $(th);
    var width = $th.width();
    $th.width(width);
});

For 100 columns, this takes too much amount of time (more than 60s). Each "get width" call takes 500-700 ms. This is because it needs to generate layout every time, I get width.

Question - Is there a faster way to get width?

Reason - I'm freezing header. For that I need to set width of each header to fixed pixel width. Everything is dynamic and pre-setting the width of each column in px is not an option.

Tejas
  • 1,011
  • 3
  • 11
  • 16
  • 2
    Why can't you do this with CSS? – Blender Jul 12 '12 at 00:23
  • You could get a minor improvement by using a standard `for` loop and doing away with the `width` variable by setting `$th.width($th.width());`. I have a kind of half-baked idea about copying the header row into another `visibility:hidden` table and getting the widths from that table - perhaps that would be faster since you'd be getting widths from a one-row table. I'm surprised that _getting_ the width takes so much longer than _setting_ the width. – nnnnnn Jul 12 '12 at 00:32
  • Can you explain what you mean by "freezing header"? Does that mean that nothing in a given column can exceed the width of its header? – lbstr Jul 12 '12 at 00:45
  • @Blender How would you do that with CSS? – lbstr Jul 12 '12 at 01:24
  • i load tested the snot out of your code and it runs about as fast as you can get it to run using jQuery. – Cory Danielson Jul 12 '12 at 02:57
  • Try native JavaScript: `elem.offsetWidth = elem.offsetWidth;` Ohh @ajax81 already mentioned that. – Johannes Egger Jul 12 '12 at 04:46
  • "Freezine header" means thead remains in place, while user is able to scroll through rows. One standard-compliant way of doing this is - take thead out and put it in another div on top of original table. That's why I need above code to fix the width of all cells, so that it stays in alignment with data columns. – Tejas Jul 12 '12 at 14:49
  • doing it in css "$th.css("width")" or doing it in plain javascript does not help me much, since it reflows anyway. I tried detach that results in all widths = 0, so not an option. I'll try visibility:hidden but I'm afraid it may be same as detach. I will keep you posted. – Tejas Jul 12 '12 at 14:52

2 Answers2

3

You might consider writing plain vanilla javascript for this. Might shave some milliseconds off your time.

var cells = myTable.rows[0].children;

for ( var i = 0; i < cells.length; i++ ){
   cells[i].style.width = cells[i].offsetWidth + "px";
}

Because you're using offsetWidth, you're not going to be able to get away from reflow. However, estimating the cell widths based on how many characters are in the TH (assuming the text doesn't wrap and you're using a fixed, monotyped font) and creatively applying CSS can greatly reduce reflow latency.

// Appends CSS Classes to stylesheet in doc head
function appendStyle(styles) {
  var css = document.createElement('style');
  css.type = 'text/css';

  // Browser compatibility check
  if (css.styleSheet) css.styleSheet.cssText = styles;
  else css.appendChild(document.createTextNode(styles));

  document.getElementsByTagName("head")[0].appendChild(css);
}

var cells       = myTable.rows[0].children;
var cellWidth   = 0;
var letterWidth = 5; // let's assume each letter is 5px wide
var classname   = '';
var styles      = '';

for ( var i = 0; i < cells.length; i++ ){
   classname         = "Cell" + i;
   cell[i].className = classname;
   cellWidth         = (letterWidth * cells[i].innerHTML.length);
   styles           += "." + classname + "{width:" + cellWidth + "px}";
} 

window.onload = function() { appendStyle(styles) };

You could take this even further by using document fragments (great writeup on performance benefits by John Resig) by rendering the whole things outside of the DOM at runtime and swapping the fragment in after the DOM is loaded...but seriously, at this point you've really got to ask yourself if the juice is worth the squeeze.

Community
  • 1
  • 1
Daniel Szabo
  • 7,181
  • 6
  • 48
  • 65
  • Thanks for taking time to answer. Using native js does not help much. My table is dynamically created after the page loads, so I have to call appendStyles after that. Idea of calculating width approximately assuming width of each letter has some potential. I'll try it. Freezing header is very important for business users of this enterprise application. We are creating hundreds of different views (grids) dynamically using same page. All effort is definitely worth it. – Tejas Jul 12 '12 at 15:00
  • @Tejas: No problem. I'm really curious to see how you solve this problem. Can you post your solution as an edit to your question when you've solved it? – Daniel Szabo Jul 12 '12 at 17:30
0

You could add an onload callback to each column individually where you set up its width. This way, instead of in a loop it happens as they load. Just an idea, but it could work.

HRÓÐÓLFR
  • 5,842
  • 5
  • 32
  • 35