CSS table-row creates a table on-the-fly

A strange issue with the way browsers render CSS table-row elements

At work we have a table of data where we expand an item of the table (when clicked) by inserting a row in between, and filling that new row with more detailed information (using jQuery).

The new row needs to span the complete width of the table, and since we had some issues with getting this to work I thought I'd document my findings for others that might be going through similar issues.

Bear in mind that we're styling the table, so some of the issues might be more/less pronounced depending on how much styling you're doing.

HTML tables redraw when inserting rows

We started off by trying HTML tables, since our data is pretty much tabular, and having real table markup seemed logical, and we'd then have 'free' functionality of the columns lining up.

<table>
    <thead>
        <tr>
            <th>Header</th>
            <th>Header</th>
            <th>Header</th>
            <th>Header</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Row 1</td>
            <td>Row 1</td>
            <td>Row 1</td>
            <td>Row 1</td>
        </tr>
        <tr>
            <td colspan="4">Full width row (inserted dynamically)</td>
        </tr>
        <tr>
            <td>Row 2</td>
            <td>Row 2</td>
            <td>Row 2</td>
            <td>Row 2</td>
        </tr>
    </tbody>
</table>

This approach worked on desktop browsers but in Safari on iOS (iPad with iOS 5 & 6), the insertion process caused a very strange redrawing issue which you could see quite clearly for just less than a second each time.

Swapping to CSS tables

The most simple iteration from the HTML table was to swap all elements to DIVs and then style them as a table using CSS3 display: table, display: table-row and display: table-cell styles.

<div class="table">
    <div class="row head">
        <div class="cell">Header</div>
        <div class="cell">Header</div>
        <div class="cell">Header</div>
        <div class="cell">Header</div>
    </div>
    <div class="row">
        <div class="cell">Row 1</div>
        <div class="cell">Row 1</div>
        <div class="cell">Row 1</div>
        <div class="cell">Row 1</div>
    </div>
    <div class="row">
        <div class="fullwidth-cell">Full width row (inserted dynamically)</div>
    </div>
    <div class="row">
        <div class="cell">Row 2</div>
        <div class="cell">Row 2</div>
        <div class="cell">Row 2</div>
        <div class="cell">Row 2</div>
    </div>
</div>

.table {
  display: table;
  width: 100%;
}
.row {
  display: table-row;
  width: 100%;
}
.cell {
  display: table-cell;
  width: 25%;
}
.fullwidth-cell {
  /* OPTION 1 */
  display: block; /* or without display definition */

  /* OPTION 2 */
  display: table-cell;

  width: 100%;
}

This is where the tricky issue reared it's head.

Option 1 - display: block

We first choose to make the full-width cell a block element (and take out the row container). Again, there didn't seem to be a major issue with desktop browsers, although occasionally some of the table looked misaligned after a full-width div had been inserted and subsequently removed.

On the iPads the issue was even worse though. Each time an expanded row was inserted, it would be placed above the table rather than in-between the rows, where it was situated within the DOM. My hypothesis at this point was that it's treating the CSS table as if it were an HTML table, and the inserted block element got forced outside of the table, just as a DIV in between HTML table rows would be, and instead rendered above the table.

Knowing that it's not necessary to exactly match the table -> row -> cell heirachy in CSS we decided to remove the display: table definition from the CSS. We expected that each row would then be an independant block, and although we'd have to be more careful to make sure column boundaries lined up between rows, we should be able to insert block elements without issue because each row would be an independant table-row block without a table linking them together.

The issue was the same, misalignment and occasional other visual glitches on desktop, and the block is inserted above the table on iPad.

Option 2 - display: table-cell

We then decided to try changing from block to table-row or table-cell with width: 100%. However, any combination of this CSS caused the full-width block to be the same width of the first cell on the row above/below, so couldn't be used.

You might wonder how you can make it do the same as colspan="4" from the HTML example in CSS... you can't, which is very annoying. This also exacerbates the issue with the table-rows being connected together on-the-fly by the browser.

So what's actually happening?

Based on experiments, it appears that the browser rendering engine (at least Webkit) detects sibling table-row elements, and rather than rendering them as ad-hoc one-row tables, it groups them together to create a proper table. This means that overflowing content that makes a cell wider also affects the rest of the table.

At least on desktop (Chrome), when a block element is inserted in between table-row elements, the rendering engine breaks up the table it's made for itself into two tables, one above and one below the inserted block, with their own column width matching.

When you change the inserted block to a table-row and table-cell, the browser sees a single cell on a row, and since there is no colspan property, matches the cell to the first column, and sets the width property likewise to match the rest of the table.

Solution

Remove the table-row CSS, and instead rely on block and the fact that the cell widths add up to 100% to deal with the row separation. The full-width block can then be added without major issues.

Conclusion

Check your CSS widths carefully. We had filled the row completely with content, so had very little room to maneuvre in order to keep all columns on all rows (especially since we use percentage widths rather than pixels). Don't forget to account for padding, margin and borders in your calculations, although if you're using percentage widths tracking the few pixels added by borders is difficult/impossible purely in CSS.

Use a grid where possible. We couldn't easily use a grid framework as each cell needed to be a different width because of the content, but if it would perhaps have made life easier if we could have found a grid system that worked.

Comments

thanx for ur help....
Tuesday 26 March 2013 10:43 | user icon lesscss

If you don't have a Google+ account you can comment using the normal comment form below







About the author

Portrait of the author

On weekdays I'm a Senior Software Engineer at Comparethemarket.com, having previously been a Solution Architect at Nokia & Nokia Siemens Networks, creating creative software solutions for mobile operators around the world.

In my spare time I'm an avid new technology fan, and constantly strive to find innovative uses for the new gadgets I manage to get my hands on. Most recently I've been investigating Mobile Codes, RFID and Home automation (mainly Z-Wave). With a keen eye for usability I'm attempting to create some cost-effective, DIY technology solutions which would rival even high-end retail products. The software I develop is usually released as Open Source.

I have a Finnish geek partner, so have begun the difficult task of learning Finnish.


Google+
Add me to your circles on Google+

The blog
Calendar
October 2014
MTWTFSS
29 30 01 02 03 04 05
06 07 08 09 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 01 02
Mobile

Zap the link below with your qrcode enabled mobile to send this page to it
Mobile Code for this page
What's this?