6

I'm trying to make a printable document with some quite long tables. And sometimes page ends right between table headers and the data, which makes it harder to read.

Example

How could I prevent that?

I've tried to use the following CSS but it didn't help.

@media print {
        h1, h2, h3, thead, thead>tr {
            page-break-after: avoid;
            page-break-inside: avoid;
        }

        tbody {
            page-break-before: avoid;
        }

        /* I would also like to not have page breaks within first five rows */
        tbody:nth-child(-n+5) {    
            page-break-before: avoid;
        }
}

Table structure:

<table border="1" cellspacing="0" cellpadding="3">
    <thead>
    <tr>
        <th rowspan="2">Metric</th>
        <th colspan="3">Type 1</th>
        <th colspan="3">Type 2<th>
    </tr>
    <tr>
        <th>Initial</th>
        <th>Final</th>
        <th>Difference</th>
        <th>Initial</th>
        <th>Final</th>
        <th>Difference</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>Dataset1</td>
        <td>*DATA*</td>
        ...
    </tr>
    </tbody>
</table>
Arghavan
  • 1,125
  • 1
  • 11
  • 17
Drath Vedro
  • 330
  • 2
  • 10

2 Answers2

3

I found a workaround for this issue, based on the solution suggested here: How do I avoid a page break immediately after a heading

Add a wrapper to your table and add a before pseudo-element to it. This element won't actually take up any space (due to the negative margin-bottom), but its height will be used when calculating where to put the page break, forcing the browser to push the table to the next page if it's too close to the bottom.

200px should be replaced with a value that is slightly more than the height of the header + the height of the first row of the body.

/* This is the solution */
.wrapper::before {
    content: "";
    display: block;
    height: 200px;
    margin-bottom: -200px;
    page-break-inside: avoid;
    break-inside: avoid;
}

/* Table styles */
table {
    width: 100%;
}

thead {
    background: green;
}

thead tr {
    page-break-inside: avoid;
    break-inside: avoid;
}

tbody {
    background: yellow;
}

tbody tr {
    height: 80px;
}

td {
    height: 80px;
}
<div class="wrapper">
    <table>
        <thead>
            <tr>
                <td>header</td>
                <td>header</td>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>2</td>
            </tr>
            <tr>
                <td>1</td>
                <td>2</td>
            </tr>
            <tr>
                <td>1</td>
                <td>2</td>
            </tr>
            <tr>
                <td>1</td>
                <td>2</td>
            </tr>
            <tr>
                <td>1</td>
                <td>2</td>
            </tr>
        </tbody>
    </table>
</div>
UnShame
  • 66
  • 3
-2

Apply page-break to a block-level pseudo-element on your tbody instead of directly applying it to tbody. Here is working Demo

You must carefully define your page-context and appropriate margins and dimensions to suit your use-case.

table, th, td { border: 1px solid gray; border-collapse: collapse; }
th, td { padding: 8px; }
tbody:first-of-type { background-color: #eee; }

@page {
    size: A4;
    margin: 0;
}
@media print {
    html, body {
        width: 210mm;
        height: 297mm;
    }
    tbody::after {
        content: ''; display: block;
        page-break-after: always;
        page-break-inside: avoid;
        page-break-before: avoid;        
    }
    
  
}
<div> 
    <a href="#" onClick="window.print();">Print</a>
</div>
<hr />
<table>
    <thead>
  <tr>
   <th>Head 1</th>
   <th>Head 2</th>
   <th>Head 3</th>
  </tr>
    </thead>
 <tbody>
  <tr>
   <td>Row 1 Cell 1</td>
   <td>Row 1 Cell 2</td>
   <td>Row 1 Cell 3</td>
  </tr>
  <tr>
   <td>Row 2 Cell 1</td>
   <td>Row 2 Cell 2</td>
   <td>Row 2 Cell 3</td>
  </tr>
 </tbody>
 <tbody>
  <tr>
   <td>Row 3 Cell 1</td>
   <td>Row 3 Cell 2</td>
   <td>Row 3 Cell 3</td>
  </tr>
  <tr>
   <td>Row 4 Cell 1</td>
   <td>Row 4 Cell 2</td>
   <td>Row 4 Cell 3</td>
  </tr>
 </tbody>
    <tfoot>
  <tr>
   <th>Foot 1</th>
   <th>Foot 2</th>
   <th>Foot 3</th>
  </tr> 
    </tfoot>
</table>