I have a table of data which is generated dynamically with Javascript. Every few minutes the page refreshes the data by firing off an Ajax request, getting the data back from the server, and replacing the data in the table. This is pretty standard, and the table ends up looking like this:
This works just fine if I generate the data by emptying the table and gradually adding the rows back in. However, this table can potentially have thousands of rows, and it can potentially take so long to generate the table that the browser gives the user a "This script is taking too long to execute" errors. So I fixed this by breaking the table generation into chunks and doing a bit at a time using setInterval
.
This worked fine, but because the table can take awhile to be totally generated, I tried to be clever and do some pseudo-double-buffering. I have a second table which has its display
set to none
to hide it, and when I re-generate the table I add the rows to the hidden table a bit at a time. This way the existing data is visible to the user until the table re-generation is complete, at which point we just replace it with our new content all at once.
I'm doing my replacement with the following line of code
$("#loading_area tbody").children().appendTo( $("#unplanned tbody").empty() );
This works just fine on Firefox, Safari, and Google Chrome. But on IE, I get the following:
These rows are actually not blank - the content is there if I scroll horizontally enough:
It seems that the first column is over 55,000 pixels wide! And here's the really weird part: the content re-displays properly as soon as I change ANYTHING about the style of the table. So if I change the font color to green, IE will immediately re-render the table, correctly.
But I can't make the change directly. So if I say
$("#unplanned").css("color", "green");
then it doesn't re-render properly; the color changes, but the first column stays 55,000 pixels wide. But if I make the change directly to the stylesheet
document.styleSheets[1].rules[3].style.color = "green";
then it re-renders the table, correctly.
So I ended up fixing this by by making a random style change, toggling the margin of the Expand/Collapse button between 1px
and 0px
, every time I'm finished laying out the table, and this worked.
My problem is that when I try to print the page, the rows are blank-looking, because the page content is improperly rendered to the printer.
So I'll be trying more trickery, probably just toggling which table is displayed and swapping their id
s or whatever gets this to work. My question is, what's going on here? This seems like a bug in IE; I'm using IE8 but this same thing happens on IE6 and IE7. I'd like to avoid falling into this pit in the future, but I'm not sure what's causing this, so I'm not really sure what I should be avoiding. Any light that anyone could shed on this would be much appreciated.
EDIT: Swapping which table is displayed makes the rendering problem go away in the browser without needing a stylesheet hack, but the problem with printing is still there. In fact, the printing problem is there even when the table is generated directly with no show/hide or element movement trickery at all. So I'm definitely confused and not sure what I can do to make this problem go away. I may have to make a separate, static page just for printing if I can't figure this out.
Can't say for sure without a test case, but in general:
Make sure you are using table-layout: fixed
. IE is bad at guessing auto
column widths, especially when there are colspans involved. It's also slow at guessing widths when there are many rows. Take this out of IE's hands by setting explicit sizes for each column with a fixed table layout.
Avoid appending rows to large tables. When you say children().appendTo()
jQuery is still doing each append one at a time, which soon gets very slow indeed. The best thing you can do to speed up table manipulations is to set the whole lot in one go using innerHTML
on the table's parent element. Of course preparing the HTML string can be annoying if you want to insert data (attribute values and content), since it has to be HTML-escaped. A workaround is to write all the rows at once with placeholder values, and then on a second pass fill in the real data through setting .data
on Text nodes and so on. You will need to re-attach any event handlers you're putting on those buttons in this step too, unless you are using live.
I think by writing the rows with innerHTML you should be able to speed it up enough to get rid of the timeout-based partial updates (which would seem to have nasty potential race conditions). Of course the DOM-based alternative would be to only update the rows that have changed. When you are using fixed layout, since the widths don't depend on the content, you can break your table up into several tables after each other. They'll look like they're one table, but you can deal with each table separately, either writing its innerHTML in one go, or appending its rows without the DOM performance death you get from having thousands of rows in a single table.
You could just have two tbody
elements in your primary table. One would be used for displaying, and the other for buffering. That way you could skip appending the other tbody
and just remove display.none
from the buffer to display it. My guess is that it could be a little faster and perhaps solve the glitch in IE.
An another method (trick?) for showing or hiding elements in a table I've learned is setting position
to absolute
on a row or tbody
to hide it, instead of using display
. That still hides it, but without altering (messing up) the table's layout. You could try using it to hide the buffer.
Generally, tables are one the most quirky elements there are, even in the modern browsers. Doing anything advanced with them usually leads to these little headaches, so good luck.
Have you got a width set on the table? It occurs to me that if you're generating the table while it's hidden it may not be picking up things like the current browser window width which it would normally use to set the width of a table, so it falls back to the maximum.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With