Use Styles To Dynamically Create Headers, Footers, and Page Breaks

Written By Alla Levin
August 05, 2005

If you want to force widths and page breaks on an HTML page, Phillip Perkins explains how you can do it. He walks you through creating the headers and footers necessary to insert these items to dynamically create a formatted hard copy.

While perusing a discussion board, I read a question from a concerned developer trying to programmatically create headers and footers and insert page breaks on an HTML page. This is next to impossible, considering what your output is since there’s no easy way to figure out the browser’s current settings for margins, page sizes, etc. However, if you want to force widths and page breaks, you can do it.

One way to accomplish this is to use TABLEs. In this article, I’ll show you how to create the headers and footers necessary to insert these items to create a formatted hard copy dynamically. (Note: I used IE 6.0 to test the code in this tip. It also works on 5.0 and above because I use the @media pseudo-class available for the STYLE element in IE since 5.0.)

The easiest way to create this functionality is to use TABLEs. However, your formatted output must contain individual rows that don’t exceed the printed page’s height. Also, your TABLE must not exceed the width of the printed page. Since you don’t have any control over landscape or portrait, you must make some assumptions. You’ll assume that the orientation of the document is a portrait with 1″ margins left and right, 0.25″ margins top and bottom, and the paper size is 8.5″ X 11″. This means that you have 6.5″ total width for output, so your TABLE width will be set to 6.5in, with no border. At the top of the table, you will create two hidden DIVs: header and footer. The DIVs have set widths and heights, and the overflow style attribute is set to hidden. This sets the foundation for your output. To create the page breaks, headers, and footers, you must incorporate some JavaScript after the page loads.

Use styles to create headers dynamically

The idea for creating the formatted output is to copy the header and footer DIVS into hidden rows. However, you’ll set the display style attribute to inline for @media print. Also, you’ll set the page-break-after style attribute to always for the TR just before the footer.

During the BODY on load event, iterate through all the rows to determine whether the row offset top plus the offset height exceeds the available length of the page. The available length is the length of the page minus the sum of the heights of the header, footer, and top and bottom margins. So, if the header height and footer heights are each 1″, the top and bottom margins are 0.25″, and the page length is 11″, then the available width is 8.5″: 11 – (1 x 2) – (0.25 x 2) = 8.5.

Listing A contains the HTML that accomplishes this task. When you examine the sample code, you’ll notice that the header and footer are two hidden DIVs that appear right before the TABLE element. The TABLE tbl1 contains multiple TRs. Each row is identified as t1 to help iterate through them using the all collection. This example will only work with two or more rows. I created the style class hdrftr to identify when those items should be displayed. The JavaScript details how the functionality is created.

First, a header row is added to the top of the table. Next, each row is examined to determine if the top coordinate plus the row height plus the footer height — 1in * screen.deviceYDPI or just screen.deviceYDPI — meets or exceeds the total available height. If this value matches the available height, the current row is set to have a page break after it. If this value exceeds the available height, the previous row is set. The row of interest is then used to append a footer row after it and a header row is added after the footer row.

This process continues until all rows are examined. Finally, a footer row is appended to the end of the table. The footer row contains absolute positioning to avoid inadvertent page breaks from the browser, thus giving us a little margin of error. Also, each subsequent page must make a provision for the additional header rows that we add: (page – 1) * 1, where (page – 1) is the subsequent page number and 1 is the height of the header.

<html>
<head>
<title></title>
<style>
@media print {
.hdrftr { display:inline; }
}
@media screen {
.hdrftr { display:none; }
}
</style>
</head>
<body id="b1" style="width:6.5in;">
<script for="b1" event="onload">
var trHeader = document.createElement("TR");
trHeader = tbl1.children[0].insertBefore(trHeader, tbl1.all["t1"][0]);
trHeader.style.height = "1in";
trHeader.className = "hdrftr";
var tdHeader = document.createElement("TD");
tdHeader = trHeader.appendChild(tdHeader);
var hdr = document.all["hdr"].cloneNode(true);
hdr = tdHeader.appendChild(hdr);
hdr.id = document.uniqueID;
hdr.style.display = "inline";
var page = 1;
for (var i = 0; i < tbl1.all["t1"].length; i++) {
var trOfInterest = null;
if (parseInt(tbl1.all["t1"][i].offsetTop) +
parseInt(tbl1.all["t1"][i].offsetHeight) == (8.5 * screen.deviceYDPI * page))
{
trOfInterest = tbl1.all["t1"][i];
}
if (parseInt(tbl1.all["t1"][i].offsetTop) +
parseInt(tbl1.all["t1"][i].offsetHeight) > (8.5 * screen.deviceYDPI * page)) {
trOfInterest = tbl1.all["t1"][i-1];
i--;
}
if (trOfInterest != null) {
trOfInterest.children[0].innerText += " changed";
trOfInterest.style.pageBreakAfter = "always";
var trFooter = document.createElement("TR");
trFooter = tbl1.children[0].insertBefore(trFooter,
trOfInterest.nextSibling);
trFooter.className = "hdrftr";
trFooter.style.height = "1in";
var tdFooter = document.createElement("TD");
tdFooter = trFooter.appendChild(tdFooter);
var ftr = document.all["ftr"].cloneNode(true);
ftr = tdFooter.appendChild(ftr);
ftr.id = document.uniqueID;
ftr.style.display = "block";
trFooter.style.position = "absolute";
trFooter.style.top = ((9 * page) + ((page - 1) * 1)) + "in";
trFooter.style.left = "0in";
var trHeader = document.createElement("TR");
trHeader = tbl1.children[0].insertBefore(trHeader,
trFooter.nextSibling);
trHeader.style.height = "1in";
trHeader.className = "hdrftr";
var tdHeader = document.createElement("TD");
tdHeader = trHeader.appendChild(tdHeader);
var hdr = document.all["hdr"].cloneNode(true);
hdr = tdHeader.appendChild(hdr);
hdr.id = document.uniqueID;
hdr.style.display = "inline";
page++;
}
var trFooter = document.createElement("TR");
trFooter = tbl1.children[0].appendChild(trFooter);
trFooter.className = "hdrftr";
trFooter.style.height = "1in";
var tdFooter = document.createElement("TD");
tdFooter = trFooter.appendChild(tdFooter);
var ftr = document.all["ftr"].cloneNode(true);
ftr = tdFooter.appendChild(ftr);
ftr.id = document.uniqueID;
ftr.style.display = "inline";
trFooter.style.position = "absolute";
trFooter.style.top = ((9 * page) + ((page - 1) * 1)) + "in";
trFooter.style.left = "0in";
}
</script>
<div id="hdr" style="display:none;width:6.5in;height:1in;">Header</div>
<div id="ftr"
style="display:none;width:6.5in;height:1in;left:0px;top:9.5in;">Footer</div>
<table id="tbl1">
<tr id="t1">
<td>Test</td>
</tr>
<tr id="t1">
<td>Test</td>
</tr>
...
</table>
</body>
</html>

I Need More

Enter your Email Address to Join the
Gang of Curious and Life Loving
People!

Related Articles