How to convert HTML tables to CSS

 

How to convert HTML tables to CSS

After explaining in our previous article why it is generally a good idea to get rid of HTML tables it is time to give some practical advise on how to actually convert the tables to tableless HTML and CSS code. In this article we describe a couple of conversion methods that are generic and can be applied to any web page.

First we need to decide which HTML tag or tags could be used to replace the table elements – TABLE, TR, TD (for simplicity’s sake we will ignore other seldom used table elements like THEAD, TFOOT, TH and TBODY). We choose the replacement tag based on the following criteria:

  • The replacement tag should have as few side effects as possible, i.e. we do not want it to affect the style of the inner tags and text, because we will be using CSS styles to define the appearance of the enclosed tags and text.
  • The replacement tag should have a block style just like the replaced TABLE, TR and TD tags.

From all HTML elements DIV and SPAN elements are the only two tags that do are used purely for grouping child elements and text and do not affect the CSS style of their nested elements. Of these two elements DIV is a block element and SPAN is an inline element. This leaves us with DIV as the natural replacement for the table elements.

So lets say that we have this sample HTML code:

<html>
<body>
<table width="600">
	<tr>
		<td width="50%">Cell 1</td>
		<td width="50%">Cell 2</td>
	</tr>
	<tr>
		<td>Cell 3</td>
		<td>Cell 4</td>
	</tr>
	<tr>
		<td>Cell 5</td>
		<td>Cell 6</td>
	</tr>
</table>
</body>
</html>

We will try to convert the HTML tables in this sample code to DIV tags using CSS styles for layout.

The first approach

(this was the approach used by the first version of our Table2CSS converter)

We will try to mimic the table layout as closely as possible using DIV elements. It is quite easy to replace the TABLE and TR tags:

<html>
<body>
<div style="width: 600px;">
	<div>
		...
	</div>
	<div>
		...
	</div>
	<div>
		...
	</div>
</div>
</body>
</html>

The TD elements however are a bit more problematic to convert. Each DIV element forms a new block so it is not so easy to place more than one DIV element on the same line. Applying floating style however allows us to change the DIV element behavior and to place several DIVs side by side:

<html>
<body>
<div style="width: 600px;">
	<div>
		<div style="float: left; width: 50%;">Cell 1</div>
		<div style="float: left; width: 50%;">Cell 2</div>
	</div>
	<div style="clear: both;">
		<div style="float: left; width: 50%;">Cell 3</div>
		<div style="float: left; width: 50%;">Cell 4</div>
	</div>
	<div style="clear: both;">
		<div style="float: left; width: 50%;">Cell 5</div>
		<div style="float: left; width: 50%;">Cell 6</div>
	</div>
</div>
</body>
</html>

Please note that our pseudo table from the example above shows the following peculiarities:

  • When their width is not specified DIV elements tend to expand to take all available horizontal space. Unlike DIVs real tables tend to shrink horizontally and take just enough horizontal space as to display their content. Since there is no easy and portable way to simulate that behavior with DIV elements we specify the table width explicitly.
  • Table cells form a grid in which all cells in a given column have the same width and all cells in a given row have the same height. Again there is no portable way to simulate this behavior on all browsers so we force equal cell widths by specifying them explicitly.
  • We use the “clear: both” style in order to specify that the DIV emulating the table row will be placed on the next line.
  • For our example we use inline styles, but if your HTML file contains many tables, rows and cells you will probably be better off moving the inline styles to embedded or external CSS styles.

The second approach

(this is the approach used by the second version of our Table2CSS converter)

Another possible solution is to use DIV elements only for the table and the cells and completely ignore the TR elements. In this case we will have to use absolute positioning in order to place the cells and our sample table will be converted to:

<html>
<body>
<div style="position: relative; width: 600px; height: 60px;">
	<div style="position: absolute; left: 0px; top: 0px; width: 300px; height: 20px;">Cell 1</div>
	<div style="position: absolute; left: 300px; top: 0px; width: 300px; height: 20px;">Cell 2</div>
	<div style="position: absolute; left: 0px; top: 20px; width: 300px; height: 20px;">Cell 3</div>
	<div style="position: absolute; left: 300px; top: 20px; width: 300px; height: 20px;">Cell 4</div>
	<div style="position: absolute; left: 0px; top: 40px; width: 300px; height: 20px;">Cell 5</div>
	<div style="position: absolute; left: 300px; top: 40px; width: 300px; height: 20px;">Cell 6</div>
</div>
</body>
</html>

This solution has its own set of peculiarities worth noting:

  • We are positioning all cells absolutely. This means that we need to know the widths and heights of all cells beforehand because we will use these dimensions in order to specify the cell width, height and position in its CSS style.
  • This solution shows clearly that there is a trade-off between the number of CSS style rules and the number of HTML elements. The second approach minimizes the number of HTML tags but increases the number of CSS style rules. The original solution using tables had a total of 10 elements (one table, tree rows and six cells), while the new solution has only seven DIV elements. However the tableless code also adds seven CSS style rules.
  • Just like with the first solution it is better to move the inline styles to embedded or external ones.

Possible optimizations

  • The first optimization of the above code would be to move inline styles to an extternal file so that web crawlers that do not understand CSS would not have to download the CSS styles. In this case our HTML code would look like:
    <html>
    <head>
    <link href="style.css" rel="stylesheet" type="text/css">
    <head>
    <body>
    <div class="main">
    	<div class="cell_1_1">Cell 1</div>
    	<div class="cell_1_2">Cell 2</div>
    	<div class="cell_2_1">Cell 3</div>
    	<div class="cell_2_2">Cell 4</div>
    	<div class="cell_3_1">Cell 5</div>
    	<div class="cell_3_2">Cell 6</div>
    </div>
    </body>
    </html>

    And we would have an external file called style.css that would contain:

    .main {position: relative; width: 600px; height: 60px;}
    .cell_1_1 {position: absolute; left: 0px; top: 0px; width: 300px; height: 20px;}
    .cell_1_2 {position: absolute; left: 300px; top: 0px; width: 300px; height: 20px;}
    .cell_2_1 {position: absolute; left: 0px; top: 20px; width: 300px; height: 20px;}
    .cell_2_2 {position: absolute; left: 300px; top: 20px; width: 300px; height: 20px;}
    .cell_3_1 {position: absolute; left: 0px; top: 40px; width: 300px; height: 20px;}
    .cell_3_2 {position: absolute; left: 300px; top: 40px; width: 300px; height: 20px;}
  • Another possible optimization is merging nested DIV elements. For example this table:
    <table width="600"><tr><td width="50%">Cell</td></tr></table>

    can be replaced with the following tableless code:

    <div style="position: relative; width: 600px; height: 20px;">
    	<div style="position: absolute; left: 0px; top: 0px; width: 300px; height: 20px;">Cell 1</div>
    </div>

    which after we merge the nested DIVs will become:

    <div style="width: 600px; height: 20px;">Cell</div>

Leave a comment