Printing table headers on every page

Tables that expand over one page when printed can be a litle confusing, especially when lots of columns are involved, because column headers are not displayed on every page. One may have to flip back to the first page to make sense of the data. Not to mention the need for better looking reports given out to magement or clients. It’s possible though to print table headers on every page, and we’ll see how to do that with a little bit of CSS.

Our base table, unedited

We’ll demonstrate the use of this functionality by using a simple table that extends over many pages. First, let’s see the table in the old-fashioned way that we are all familiar with:

<table border="1" cellpadding="2" cellspacing="0">
	<tr>
		<th>ID</th>
		<th>First Name</th>
		<th>Last Name</th>
	</tr>
	<cfoutput>
		<cfloop from="1" to="50" index="i">
			<tr>
				<td>#i#</td>
				<td>First Name #i#</td>
				<td>Last Name #i#</td>
			</tr>
		</cfloop>
	</cfoutput>
</table>

Although the loop code is written in Coldfusion code, that is of no significance here. You can perform your loop in any language you like. The main point to note is the result you get when you try to print this long table. Here’s what the second page looks like when printed in IE 6.0:

It’s easy to see the problems:

  1. No headers on subsequent pages.
  2. The table borders may break as well.

Adding page headers to the table

So, we set out to fix those two issues. There are three things that we need to do to get this to work:

  1. Wrap the table headers that we want to display on every page in a thead tag.
  2. Wrap the data content of our table in a tbody tag.
  3. Add CSS properties for those two extra tags.
<style type="text/css" media="print,screen" >
th {
	font-family:Arial;
	color:black;
	background-color:lightgrey;
}
thead {
	display:table-header-group;
}
tbody {
	display:table-row-group;
}
</style>

<table border="1" cellpadding="2" cellspacing="0">
	<thead>
		<tr>
			<th>ID</th>
			<th>First Name</th>
			<th>Last Name</th>
		</tr>
	</thead>
	<tbody>
		<cfoutput>
			<cfloop from="1" to="50" index="i">
				<tr>
					<td>#i#</td>
					<td>First Name #i#</td>
					<td>Last Name #i#</td>
				</tr>
			</cfloop>
		</cfoutput>
	</tbody>
</table>

The CSS properties are declared for both media and screen. I also added a little bit of formatting for the th tag to make the table headers stand out a little better. Now, let’s see the results of printing page two in IE 6.0:

Much better now. We can actually see the table headers even if we are on page two.

You should be aware that there are browser inconsistencies with this. This was tested with IE 6.0, which needs the CSS declarations for this to work. However, it looks like Mozilla based browsers, like Firefox 1.0+, behave much more predictably and do not require the style sheet rule. No matter what though, you still need the thead and tbody declared. Footers (tfoot) should in theory work the same way, but they don’t.

Improving the table print display

Are we done here? Well, there is still one minor issue that we can deal with. Using the method above, let’s take a look at the bottom of page one while printing:

We can see from the screenshot above that the table lines extend a little below the last data row. This is happening because the last row did not exactly match the length of the page and we still have some printable space below it. If there was more printable space and our data per row expanded over more than one line, then it would show part of the row on this page and part on the next.

However, if left and right borders are not necessary in your table, we can perform a little trick that will take care of this. Let’s make two changes to our code:

  1. Set our table’s border to null.
  2. Add a border at the bottom of each data row.
<style type="text/css" media="print,screen" >
table td {
 border-bottom:1px solid gray;
}
...
</style>

<table border="0" cellpadding="2" cellspacing="0">
...

Looking at the printed table now, the results are obvious:

The screenshot shows pages 1-2. The bottom of page one now shows no evidence that there is any printable area below the last line, and page two still shows up as expected.

Please note that (even though it’s not shown here), we can still have a browser break up a row onto more than one page. Your immediate reaction might be to add this CSS rule to the tr tag to avoid this:

tr {page-break-inside: avoid;}

However, at the moment this seems to be honored only by Opera 7.5+.

Using these simple techniques one can go about creating nicer looking and better functional reports. Of course, you can take the table display up a few notches as you see fit, by adding things like alternate row colors, improved header styles, colors, etc. The possibilities there are endless and so I leave that up the reader. The base however to do all this was shown here.

10 Responses to Printing table headers on every page

  • Risul Karim

    Can you show this example for html or php file? The example you have provided is in .cfm format. If i change the extension and even add head, body like html, it does not show the right output.

    Regards
    Risul

    • Evagoras Charalambous

      I just tried the download myself, renamed the “cfm” file to “htm” and opened it. It worked fine for me. Can you double check and let me know if you still can’t get it to work?

  • Naman Aggarwal

    Thanks it helped …
    Doesnt seem to work on chrome though.

    • Evagoras Charalambous

      Unfortunately this technique works well in IE and Firefox, but not so much on Safari and Chrome.

  • Samiksha

    Its working for me. Thank you so much for this post.

    • Evagoras Charalambous

      Glad I could be of help.

  • Venkatesh

    will it work for dynamically created table?

    • Evagoras Charalambous

      I don’t see why not. As long as the DOM is correctly handled things should display properly at print-time anyway.

  • huchmampf

    This doesn’t work in chrome/chromium because of this issue:

    https://code.google.com/p/chromium/issues/detail?id=24826

    Thanks for this post.

  • Trystian

    I got table B in table A, header from A is on every page and do not break anything but header of table B break borders of table A
    display:table-row-group; not works. Any ideas ?

Leave a Reply

Your email address will not be published. Required fields are marked *