First of all, it was quite a challenge answering this question.
Secondly, why weren't the other examples just working? That's because the counter only fires on every element. When you print, the browser doesn't create new instances of <thead>
, at least to source code of your website. It just simply displays it. However, this behaviour can be bypassed by, before printing ('beforeprint'
), altering the DOM and recreating the print pages with Javascript. Now, on every page there's a new <table>
with a new instance of <thead>
. Now, the counter works. After printing this code automatically resets the DOM to the state before printing. The User Experience stays the same.
With const pixelHeightA4 = 1000;
you can specify how many pixels fit one a4. 1000 comes really close when I was testing.
With const heightOfTHead = 160;
you specify the height of the table header on an a4 size.
I hope the comments in code can explain the process.
//alter the dom to print the elements
window.addEventListener('beforeprint', event => {
//specify the number of rows in the tBody per page
//You can either hardcode this number or change it for every page with code
var table = document.getElementsByClassName('SetupMainTable')[0];
printTableData(table);
table.style.display = 'none';
});
function printTableData(table) {
//initialise the table to get the data from
var table = table.querySelectorAll('tbody')[0];
var tBody;
//get the template table
let temp = document.getElementsByTagName('template')[0];
//get the number of element for the last page
const pixelHeightA4 = 1000;
const heightOfTHead = 160;
var heightOfContent = heightOfTHead;
for (var i = 0, row; row = table.rows[i]; i++) {
//get the height of the current row
var rowHeight = row.getBoundingClientRect().height;
var nextRowHeight;
//get the height of the next row.
if (i + 1 == table.rows.length) {
nextRowHeight = 0;
} else {
nextRowHeight = (table.rows[i + 1]).getBoundingClientRect().height;
}
if (heightOfContent == 160 && table.rows.length - 1 != i) {
//if it is the first element and not the only element on the last page
//create new tBody
tBody = document.createElement('tbody');
var firstEl = row.cloneNode(true);
heightOfContent += rowHeight;
tBody.appendChild(firstEl);
} else if (((heightOfContent + rowHeight) < pixelHeightA4 && (heightOfContent + rowHeight + nextRowHeight) >= 1000) || table.rows.length - 1 == i) {
//if the row still fits on the page but the next row doesn't then this is the last table row of the page.
if (tBody == null) {
//if there's only one element on the last page, create a new tBody
tBody = document.createElement('tbody');
}
//clone the template
var clon = temp.content.cloneNode(true);
//clone the row to append to the tbody
var cloneRow = row.cloneNode(true);
//get the table element in the template
var cloneTable = clon.querySelectorAll('table')[0];
//append it all togehter
tBody.appendChild(cloneRow);
cloneTable.appendChild(tBody);
//append the table to the page
document.body.appendChild(clon);
//reset the tBody
tBody = null;
heightOfContent = heightOfTHead;
} else {
//clone a row and append it to the tBody
var cloneRow = row.cloneNode(true);
heightOfContent += rowHeight;
tBody.appendChild(cloneRow);
}
console.log(heightOfContent);
}
}
//reset the dom to the state before print
window.addEventListener('afterprint', event => {
var table = document.getElementsByClassName('SetupMainTable');
table[0].style.display = 'table';
//remove all the cloned tables
var cloneTables = document.getElementsByClassName('templateTable');
while (cloneTables.length > 0) {
cloneTables[0].parentNode.removeChild(cloneTables[0]);
}
});
@media print {
body {
counter-reset: page;
color: red;
}
.page-number:before {
counter-increment: page;
content: "Page "counter(page);
}
table {
break-after: page !important;
border: 1px solid grey;
border-collapse: collapse;
}
.templateTable {
display: table;
}
}
@media screen {
body {
counter-reset: page;
}
.page-number {
text-align: center;
}
table {
border: 1px solid grey;
border-collapse: collapse;
}
thead {
display: table-header-group;
}
.page-number:before {
counter-increment: page;
content: "Page "counter(page);
}
.templateTable {
display: none;
}
}
table td,
table th {
border: 1px solid black;
}
table tr:first-child th {
border-top: 0;
}
table tr:last-child td {
border-bottom: 0;
}
table tr td:first-child,
table tr th:first-child {
border-left: 0;
}
table tr td:last-child,
table tr th:last-child {
border-right: 0;
}
<!-- Extra big table with different row seizes for demonstration purposes only -->
<body>
<table class="SetupMainTable">
<thead>
<tr>
<th colspan="4">Company Name</th>
<th colspan="5" class="right" style="font-size:25px;">Daily Time Ticket</th>
<th colspan="1">
<div class="page-number"></div>
</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="5">123</td>
<td colspan="5">abc</td>
</tr>
<tr>
<td colspan="5">456</td>
<td colspan="5">def</td>
</tr>
<tr>
<td colspan="5">
<h3>header for random dynamic height</h3>
</td>
<td colspan="5">ghi</td>
</tr>
<tr>
<td colspan="5">101112</td>
<td colspan="5"><img src="https://cdn.sstatic.net/stackexchange/img/logos/so/so-logo.png" height="100"></td>
</tr>
<tr>
<td colspan="5">131416</td>
<td colspan="5">mno</td>
</tr>
<tr>
<td colspan="5">171819</td>
<td colspan="5">pqr</td>
</tr>
<tr>
<td colspan="5"><small>small text</small></td>
<td colspan="5">
<h1>Big text</h1>
</td>
</tr>
<tr>
<td colspan="5">132435</td>
<td colspan="5">vwx</td>
</tr>
<tr>
<td colspan="5">1325125123</td>
<td colspan="5">yza</td>
</tr>
<tr>
<td colspan="5">xy2532351235z</td>
<td colspan="5">bcd</td>
</tr>
<tr>
<td colspan="5">12351235125</td>
<td colspan="5">efg</td>
</tr>
<tr>
<td colspan="5">1253251235</td>
<td colspan="5">hij</td>
</tr>
<tr>
<td colspan="5">2315346</td>
<td colspan="5">klm</td>
</tr>
<tr>
<td colspan="5">123</td>
<td colspan="5">abc</td>
</tr>
<tr>
<td colspan="5">456</td>
<td colspan="5">def</td>
</tr>
<tr>
<td colspan="5">
<h3>header for random dynamic height</h3>
</td>
<td colspan="5">ghi</td>
</tr>
<tr>
<td colspan="5">101112</td>
<td colspan="5"><img src="https://cdn.sstatic.net/stackexchange/img/logos/so/so-logo.png" height="100"></td>
</tr>
<tr>
<td colspan="5">131416</td>
<td colspan="5">mno</td>
</tr>
<tr>
<td colspan="5">171819</td>
<td colspan="5">pqr</td>
</tr>
<tr>
<td colspan="5"><small>small text</small></td>
<td colspan="5">
<h1>Big text</h1>
</td>
</tr>
<tr>
<td colspan="5">132435</td>
<td colspan="5">vwx</td>
</tr>
<tr>
<td colspan="5">1325125123</td>
<td colspan="5">yza</td>
</tr>
<tr>
<td colspan="5">xy2532351235z</td>
<td colspan="5">bcd</td>
</tr>
<tr>
<td colspan="5">12351235125</td>
<td colspan="5">efg</td>
</tr>
<tr>
<td colspan="5">1253251235</td>
<td colspan="5">hij</td>
</tr>
<tr>
<td colspan="5">2315346</td>
<td colspan="5">klm</td>
</tr>
<tr>
<td colspan="5">123</td>
<td colspan="5">abc</td>
</tr>
<tr>
<td colspan="5">456</td>
<td colspan="5">def</td>
</tr>
<tr>
<td colspan="5">
<h3>header for random dynamic height</h3>
</td>
<td colspan="5">ghi</td>
</tr>
<tr>
<td colspan="5">101112</td>
<td colspan="5"><img src="https://cdn.sstatic.net/stackexchange/img/logos/so/so-logo.png" height="100"></td>
</tr>
<tr>
<td colspan="5">131416</td>
<td colspan="5">mno</td>
</tr>
<tr>
<td colspan="5">171819</td>
<td colspan="5">pqr</td>
</tr>
<tr>
<td colspan="5"><small>small text</small></td>
<td colspan="5">
<h1>Big text</h1>
</td>
</tr>
<tr>
<td colspan="5">132435</td>
<td colspan="5">vwx</td>
</tr>
<tr>
<td colspan="5">1325125123</td>
<td colspan="5">yza</td>
</tr>
<tr>
<td colspan="5">xy2532351235z</td>
<td colspan="5">bcd</td>
</tr>
<tr>
<td colspan="5">12351235125</td>
<td colspan="5">efg</td>
</tr>
<tr>
<td colspan="5">1253251235</td>
<td colspan="5">hij</td>
</tr>
<tr>
<td colspan="5">2315346</td>
<td colspan="5">klm</td>
</tr>
<tr>
<td colspan="5">123</td>
<td colspan="5">abc</td>
</tr>
<tr>
<td colspan="5">456</td>
<td colspan="5">def</td>
</tr>
<tr>
<td colspan="5">
<h3>header for random dynamic height</h3>
</td>
<td colspan="5">ghi</td>
</tr>
<tr>
<td colspan="5">101112</td>
<td colspan="5"><img src="https://cdn.sstatic.net/stackexchange/img/logos/so/so-logo.png" height="100"></td>
</tr>
<tr>
<td colspan="5">131416</td>
<td colspan="5">mno</td>
</tr>
<tr>
<td colspan="5">171819</td>
<td colspan="5">pqr</td>
</tr>
<tr>
<td colspan="5"><small>small text</small></td>
<td colspan="5">
<h1>Big text</h1>
</td>
</tr>
<tr>
<td colspan="5">132435</td>
<td colspan="5">vwx</td>
</tr>
<tr>
<td colspan="5">1325125123</td>
<td colspan="5">yza</td>
</tr>
<tr>
<td colspan="5">xy2532351235z</td>
<td colspan="5">bcd</td>
</tr>
<tr>
<td colspan="5">12351235125</td>
<td colspan="5">efg</td>
</tr>
<tr>
<td colspan="5">1253251235</td>
<td colspan="5">hij</td>
</tr>
<tr>
<td colspan="5">2315346</td>
<td colspan="5">klm</td>
</tr>
</tbody>
</table>
<template id="tables">
<table class="SetupMainTable templateTable">
<thead>
<tr>
<th colspan="4">Company Name</th>
<th colspan="5" class="right" style="font-size:25px;">Daily Time Ticket</th>
<th colspan="1">
<div class="page-number"></div>
</th>
</tr>
</thead>
</table>
</template>
</body>
Hope this helps, if not, please comment!
Edit1:
It automatically calculates the amount of rows that can fit on one a4 based on the row height of each individual <tr>
.
You can test it here (Tested it on chrome)