谷歌浏览器如何在打印页面时重复表头

47

我想在每个打印页面中重复显示表格的标题行,但 Google Chrome 似乎对 <thead> 标签支持不佳...有没有解决方法? 我使用的是 Google Chrome v13.0.782.215。

表格代码非常简单...没有什么花里胡哨的东西:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <style type="text/css" media="all">
           @page {
              size: landscape;
              margin-top: 0;
              margin-bottom: 1cm;
              margin-left: 0;
              margin-right: 0;
           }
           table {
               border: .02em solid #666; border-collapse:collapse; 
               width:100%; 
           }
           td, th {
               border: .02em solid #666; font-size:12px; line-height: 12px; 
               vertical-align:middle; padding:5px; font-family:"Arial";
           }
           th { text-align:left; font-size:12px; font-weight:bold; }
           h2 { margin-bottom: 0; }
       </style>
   </head>
   <body>
   <h2>Page Title</h2>
   <table>
       <thead>
           <tr class="row1">
               <th><strong>Heading 1</strong></th>
               <th><strong>Heading 2</strong></th>
               <th><strong>Heading 3</strong></th>
               <th><strong>Heading 4</strong></th>
               <th><strong>Heading 5</strong></th>
           </tr>
       </thead>
       <tbody>
           <tr class="row2">
              <td width="30">...</td>
              <td width="30">...</td>
              <td width="90">....</td>
              <td width="190">...</td>
              <td width="420">...</td>
           </tr>
           <tr class="row1">
              <td width="30">...</td>
              <td width="30">...</td>
              <td width="90">....</td>
              <td width="190">...</td>
              <td width="420">...</td>
           </tr>
           ....
       </tbody>
   </table>
   </body>
</html>

欢迎任何相关见解。


你可以检查Chrome的错误跟踪器,看看这是否是一个bug。 - Yi Jiang
5
@Yi Jiang: 我尝试过了,看起来这是一个长期存在的问题,从未得到解决...真的很令人沮丧。 - Stephen M
1
如果您关心这个 Chrome 中的 bug 被修复,请使用下面的链接进入 Bug 报告并通过点击左上角的星形图标来“点赞”它。 - Dmitri Sunshine
7个回答

53

更新2017-03-22:Chrome终于实现了重复表头!(实际上,我认为它们在一段时间前就已经实现了。)这意味着您可能不再需要此解决方案;只需将列标题放在<thead>标签中,您就可以全部设置好了。仅在以下情况下使用下面的解决方案:

  • 您在Chrome的实现中遇到了致命错误,
  • 您需要“额外功能”,或者
  • 您需要支持某些仍不支持重复标题的奇怪浏览器。

解决方案(已过时)

下面的代码演示了我发现的用于多页表格打印的最佳方法。它具有以下功能:

  • 每页都重复列标题
  • 无需担心纸张大小或能容纳多少行 - 浏览器会自动处理所有内容
  • 页面断点仅在行之间发生
  • 单元格边框始终完全关闭
  • 如果页面断点靠近表格顶部,则不会留下孤立的标题或没有数据附加的列标题(这个问题不仅限于Chrome)
  • 适用于Chrome!(以及其他基于Webkit的浏览器,如Safari和Opera)

... 以及以下已知限制:

  • 只支持一个 <thead>这似乎是你可以拥有的最多
  • 不支持 <tfoot>(尽管在 Chrome 兼容的情况下,技术上是可能的
  • 只支持顶部对齐的 <caption>
  • 表格不能有顶部或底部的 margin;要在表格上方或下方添加空白间距,请插入一个空的 div 并在其下设置 bottom margin
  • 任何影响高度的 CSS 尺寸值(包括 border-widthline-height)必须使用 px
  • 无法通过将宽度值应用于单个表格单元格来设置列宽;您应该让单元格内容自动确定列宽,或者如果需要,使用 <col> 来设置特定的宽度

  • JS 运行后表格不能(轻松地)进行动态更改

代码

<!DOCTYPE html>
<html>
  <body>
    <table class="print t1"> <!-- Delete "t1" class to remove row numbers. -->
      <caption>Print-Friendly Table</caption>
      <thead>
        <tr>
          <th></th>
          <th>Column Header</th>
          <th>Column Header</th>
          <th>Multi-Line<br/>Column<br/>Header</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td></td>
          <td>data</td>
          <td>Multiple<br/>lines of<br/>data</td>
          <td>data</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

<style>
  /* THE FOLLOWING CSS IS REQUIRED AND SHOULD NOT BE MODIFIED. */
    div.fauxRow {
      display: inline-block;
      vertical-align: top;
      width: 100%;
      page-break-inside: avoid;
    }
    table.fauxRow {border-spacing: 0;}
    table.fauxRow > tbody > tr > td {
      padding: 0;
      overflow: hidden;
    }
    table.fauxRow > tbody > tr > td > table.print {
      display: inline-table;
      vertical-align: top;
    }
    table.fauxRow > tbody > tr > td > table.print > caption {caption-side: top;}
    .noBreak {
      float: right;
      width: 100%;
      visibility: hidden;
    }
    .noBreak:before, .noBreak:after {
      display: block;
      content: "";
    }
    .noBreak:after {margin-top: -594mm;}
    .noBreak > div {
      display: inline-block;
      vertical-align: top;
      width:100%;
      page-break-inside: avoid;
    }
    table.print > tbody > tr {page-break-inside: avoid;}
    table.print > tbody > .metricsRow > td {border-top: none !important;}

  /* THE FOLLOWING CSS IS REQUIRED, but the values may be adjusted. */
    /* NOTE: All size values that can affect an element's height should use the px unit! */
    table.fauxRow, table.print {
      font-size: 16px;
      line-height: 20px;
    }

  /* THE FOLLOWING CSS IS OPTIONAL. */
    body {counter-reset: t1;} /* Delete to remove row numbers. */
    .noBreak .t1 > tbody > tr > :first-child:before {counter-increment: none;} /* Delete to remove row numbers. */
    .t1 > tbody > tr > :first-child:before { /* Delete to remove row numbers. */
      display: block;
      text-align: right;
      counter-increment: t1 1;
      content: counter(t1);
    }
    table.fauxRow, table.print {
      font-family: Tahoma, Verdana, Georgia; /* Try to use fonts that don't get bigger when printed. */
      margin: 0 auto 0 auto; /* Delete if you don't want table to be centered. */
    }
    table.print {border-spacing: 0;}
    table.print > * > tr > * {
      border-right: 2px solid black;
      border-bottom: 2px solid black;
      padding: 0 5px 0 5px;
    }
    table.print > * > :first-child > * {border-top: 2px solid black;}
    table.print > thead ~ * > :first-child > *, table.print > tbody ~ * > :first-child > * {border-top: none;}
    table.print > * > tr > :first-child {border-left: 2px solid black;}
    table.print > thead {vertical-align: bottom;}
    table.print > thead > .borderRow > th {border-bottom: none;}
    table.print > tbody {vertical-align: top;}
    table.print > caption {font-weight: bold;}
</style>

<script>
  (function() { // THIS FUNCTION IS NOT REQUIRED. It just adds table rows for testing purposes.
    var rowCount = 100
      , tbod = document.querySelector("table.print > tbody")
      , row = tbod.rows[0];
    for(; --rowCount; tbod.appendChild(row.cloneNode(true)));
  })();

  (function() { // THIS FUNCTION IS REQUIRED.
    if(/Firefox|MSIE |Trident/i.test(navigator.userAgent))
      var formatForPrint = function(table) {
        var noBreak = document.createElement("div")
          , noBreakTable = noBreak.appendChild(document.createElement("div")).appendChild(table.cloneNode())
          , tableParent = table.parentNode
          , tableParts = table.children
          , partCount = tableParts.length
          , partNum = 0
          , cell = table.querySelector("tbody > tr > td");
        noBreak.className = "noBreak";
        for(; partNum < partCount; partNum++) {
          if(!/tbody/i.test(tableParts[partNum].tagName))
            noBreakTable.appendChild(tableParts[partNum].cloneNode(true));
        }
        if(cell) {
          noBreakTable.appendChild(cell.parentNode.parentNode.cloneNode()).appendChild(cell.parentNode.cloneNode(true));
          if(!table.tHead) {
            var borderRow = document.createElement("tr");
            borderRow.appendChild(document.createElement("th")).colSpan="1000";
            borderRow.className = "borderRow";
            table.insertBefore(document.createElement("thead"), table.tBodies[0]).appendChild(borderRow);
          }
        }
        tableParent.insertBefore(document.createElement("div"), table).style.paddingTop = ".009px";
        tableParent.insertBefore(noBreak, table);
      };
    else
      var formatForPrint = function(table) {
        var tableParent = table.parentNode
          , cell = table.querySelector("tbody > tr > td");
        if(cell) {
          var topFauxRow = document.createElement("table")
            , fauxRowTable = topFauxRow.insertRow(0).insertCell(0).appendChild(table.cloneNode())
            , colgroup = fauxRowTable.appendChild(document.createElement("colgroup"))
            , headerHider = document.createElement("div")
            , metricsRow = document.createElement("tr")
            , cells = cell.parentNode.cells
            , cellNum = cells.length
            , colCount = 0
            , tbods = table.tBodies
            , tbodCount = tbods.length
            , tbodNum = 0
            , tbod = tbods[0];
          for(; cellNum--; colCount += cells[cellNum].colSpan);
          for(cellNum = colCount; cellNum--; metricsRow.appendChild(document.createElement("td")).style.padding = 0);
          cells = metricsRow.cells;
          tbod.insertBefore(metricsRow, tbod.firstChild);
          for(; ++cellNum < colCount; colgroup.appendChild(document.createElement("col")).style.width = cells[cellNum].offsetWidth + "px");
          var borderWidth = metricsRow.offsetHeight;
          metricsRow.className = "metricsRow";
          borderWidth -= metricsRow.offsetHeight;
          tbod.removeChild(metricsRow);
          tableParent.insertBefore(topFauxRow, table).className = "fauxRow";
          if(table.tHead)
            fauxRowTable.appendChild(table.tHead);
          var fauxRow = topFauxRow.cloneNode(true)
            , fauxRowCell = fauxRow.rows[0].cells[0];
          fauxRowCell.insertBefore(headerHider, fauxRowCell.firstChild).style.marginBottom = -fauxRowTable.offsetHeight - borderWidth + "px";
          if(table.caption)
            fauxRowTable.insertBefore(table.caption, fauxRowTable.firstChild);
          if(tbod.rows[0])
            fauxRowTable.appendChild(tbod.cloneNode()).appendChild(tbod.rows[0]);
          for(; tbodNum < tbodCount; tbodNum++) {
            tbod = tbods[tbodNum];
            rows = tbod.rows;
            for(; rows[0]; tableParent.insertBefore(fauxRow.cloneNode(true), table).rows[0].cells[0].children[1].appendChild(tbod.cloneNode()).appendChild(rows[0]));
          }
          tableParent.removeChild(table);
        }
        else
          tableParent.insertBefore(document.createElement("div"), table).appendChild(table).parentNode.className="fauxRow";
      };
    var tables = document.body.querySelectorAll("table.print")
      , tableNum = tables.length;
    for(; tableNum--; formatForPrint(tables[tableNum]));
  })();
</script>

工作原理(如果您不关心,请不要继续阅读;您所需的一切都在上面。)

根据@Kingsolmn的请求,以下是该解决方案的工作原理说明。它不涵盖JavaScript,尽管JavaScript能够使这种技术更易于使用。相反,重点放在生成的HTML结构和相关的CSS上,这才是真正的魔力所在。

下面是我们将要处理的表格:

<table>
  <tr><th>ColumnA</th><th>ColumnB</th></tr>
  <tr><td>row1</td><td>row1</td></tr>
  <tr><td>row2</td><td>row2</td></tr>
  <tr><td>row3</td><td>row3</td></tr>
</table>

为了节省空间,我只提供了3行数据;显然,多页表通常会有更多的数据。

我们需要做的第一件事是将表格拆分成一系列较小的表格,每个表格都有自己的列标题副本。我将这些较小的表格称为虚拟行

<table> <!-- fauxRow -->
  <tr><th>ColumnA</th><th>ColumnB</th></tr>
  <tr><td>row1</td><td>row1</td></tr>
</table>

<table> <!-- fauxRow -->
  <tr><th>ColumnA</th><th>ColumnB</th></tr>
  <tr><td>row2</td><td>row2</td></tr>
</table>

<table> <!-- fauxRow -->
  <tr><th>ColumnA</th><th>ColumnB</th></tr>
  <tr><td>row3</td><td>row3</td></tr>
</table>

The fauxRows是原始表格的克隆版本,每个只包含一行数据。(如果您的表格有标题,则只有顶部的fauxRow应该包括它。)
接下来,我们需要使fauxRows无法分割。这是什么意思?(请注意-这可能是页面分页处理中最重要的概念。)“无法分割”是我用来描述无法在两个页面之间分割的内容块的术语*。当页面断开发生在此类块所占据的空间内时,整个块将移动到下一页。(请注意,我在这里非正式地使用“块”一词;我没有特指块级元素。)这种行为具有一个有趣的副作用,我们稍后会利用它:它可以显示由于层叠或溢出而最初隐藏的内容。
我们可以通过应用以下CSS声明之一来使fauxRows无法分割:
  • page-break-inside: avoid;
  • display: inline-table;
我通常同时使用两种方式, 因为第一种是专为此目的而设计的,而第二种则适用于旧版/不兼容的浏览器。但在这种情况下,为了简单起见,我将坚持使用分页符属性。请注意,在添加此属性后,您将看不到表格外观上的任何变化。

<table style="page-break-inside: avoid;"> <!-- fauxRow -->
      <tr><th>ColumnA</th><th>ColumnB</th></tr>
      <tr><td>row1</td><td>row1</td></tr>
    </table>
    
    <table style="page-break-inside: avoid;"> <!-- fauxRow -->
      <tr><th>ColumnA</th><th>ColumnB</th></tr>
      <tr><td>row2</td><td>row2</td></tr>
    </table>
    
    <table style="page-break-inside: avoid;"> <!-- fauxRow -->
      <tr><th>ColumnA</th><th>ColumnB</th></tr>
      <tr><td>row3</td><td>row3</td></tr>
    </table>

现在,由于伪行已经无法分割,如果数据行内发生页面断开,它将与其附加的标题行一起移动到下一页。因此,下一页将始终在顶部具有列标题,这是我们的目标。但是,现在所有额外的标题行使表格看起来非常奇怪。为了使它再次看起来像一个普通表格,我们需要以这样一种方式隐藏额外的标题,即它们仅在需要时才出现。
我们要做的是将每个伪行放入带有overflow: hidden;的容器元素中,然后将其向上移动,以便标题被容器的顶部截断。这也会将数据行移回一起,以便它们连续显示。
您的第一反应可能是使用div作为容器,但我们将使用父表的单元格。稍后我会解释原因,但现在让我们添加代码。(再次强调,这不会影响表格的外观。)

table {
  border-spacing: 0;
  line-height: 20px;
}
th, td {
  padding-top: 0;
  padding-bottom: 0;
}
<table> <!-- parent table -->
  <tr>
    <td style="overflow: hidden;">

      <table style="page-break-inside: avoid;"> <!-- fauxRow -->
        <tr><th>ColumnA</th><th>ColumnB</th></tr>
        <tr><td>row1</td><td>row1</td></tr>
      </table>

    </td>
  </tr>
  <tr>
    <td style="overflow: hidden;">

      <table style="page-break-inside: avoid;"> <!-- fauxRow -->
        <tr><th>ColumnA</th><th>ColumnB</th></tr>
        <tr><td>row2</td><td>row2</td></tr>
      </table>

    </td>
  </tr>
  <tr>
    <td style="overflow: hidden;">

      <table style="page-break-inside: avoid;"> <!-- fauxRow -->
        <tr><th>ColumnA</th><th>ColumnB</th></tr>
        <tr><td>row3</td><td>row3</td></tr>
      </table>

    </td>
  </tr>
</table>

注意上面表格标记之上的CSS。我添加了它有两个原因:首先,它防止父级表格在伪行之间添加空白;其次,它使标题高度可预测,这是必要的,因为我们不使用JavaScript动态计算它。
现在我们只需要将伪行向上移动,这可以通过负边距来实现。但这并不像你想的那么简单。如果我们直接向伪行添加负边距,当伪行被推到下一页时,它仍然会生效,导致标题被页面顶部裁剪。我们需要一种方法来留下负边距。
为了实现这一点,我们将在第一个伪行之后插入一个空的div,并向其添加负边距。(第一个伪行被跳过,因为它的标题应始终可见。)由于边距在单独的元素上,它不会跟随伪行到下一页,标题也不会被裁剪。我将这些空的div称为“headerHiders”。

table {
  border-spacing: 0;
  line-height: 20px;
}
th, td {
  padding-top: 0;
  padding-bottom: 0;
}
<table> <!-- parent table -->
  <tr>
    <td style="overflow: hidden;">

      <table style="page-break-inside: avoid;"> <!-- fauxRow -->
        <tr><th>ColumnA</th><th>ColumnB</th></tr>
        <tr><td>row1</td><td>row1</td></tr>
      </table>

    </td>
  </tr>
  <tr>
    <td style="overflow: hidden;">

      <div style="margin-bottom: -20px;"></div> <!-- headerHider -->

      <table style="page-break-inside: avoid;"> <!-- fauxRow -->
        <tr><th>ColumnA</th><th>ColumnB</th></tr>
        <tr><td>row2</td><td>row2</td></tr>
      </table>

    </td>
  </tr>
  <tr>
    <td style="overflow: hidden;">

      <div style="margin-bottom: -20px;"></div> <!-- headerHider -->

      <table style="page-break-inside: avoid;"> <!-- fauxRow -->
        <tr><th>ColumnA</th><th>ColumnB</th></tr>
        <tr><td>row3</td><td>row3</td></tr>
      </table>

    </td>
  </tr>
</table>

完成了!屏幕上,表格现在应该看起来正常,只有一个顶部的列标题。在打印时,它现在应该有运行标题。

如果你想知道为什么我们使用父表而不是一堆容器div,那是因为Chrome/webkit有一个bug,导致一个被div包围的不可分割块将其容器带到下一页。由于headerHider也在容器中,它不会像预期的那样被留在原地,这导致标题被裁剪。只有当不可分割块是具有非零高度的div中最顶部的元素时,才会出现此错误。

在编写本教程时,我发现了一个解决方法:您只需在headerHider上明确设置height: 0;并给它一个具有非零高度的空子div。然后您可以使用一个div容器。不过,我仍然更喜欢使用父表,因为它经过了更全面的测试,并且通过将fauxRows重新绑定到单个表中,在某种程度上挽救了语义。

编辑:我刚意识到JavaScript生成的标记略有不同,它将每个fauxRow放在单独的容器表中,并将“fauxRow”className分配给它(容器)。这对于页脚支持是必需的,我曾经打算添加它,但从未实现。如果我要更新JS,我可能会考虑切换到div容器,因为我使用表格的语义理由不适用。
*只有当不可分块超过可打印区域的高度时,才会出现将其拆分为两个页面的情况。您应该尽量避免这种情况;您实际上要求浏览器做不可能的事情,这可能会对输出产生一些非常奇怪的影响。

1
@DoctorDestructo,我尝试实现这个解决方案,确实避免了在行内分页,但最终导致列混乱,就像这张图片:http://s11.postimg.org/uyhoktapv/error.jpg。你能帮我修复吗?提前感谢! - delphirules
2
@delphirules 确保在表格中没有应用任何<td><th>元素的宽度值。设置列宽度只有两种有效方法:1)让单元格内容自动设置宽度,或2)使用<col>元素。如果这不是问题所在,则可能需要查看您的表格html(在运行所需js函数之前)以进行诊断。 - DoctorDestructo
1
如果您的表格应用了顶部填充,那可能会导致您所描述的行为。最好的解决方法是去掉顶部填充,而是在表格上方的元素中添加底部边距。(实际上,在打印时,垂直留白最好使用底部边距)。 - DoctorDestructo
1
@VoidKing 回到 CSS 组合器的问题上,我应该更具体地说明何时避免使用它们。大多数情况下,它们可能会按预期工作;主要是当您使用它们来定位兄弟行时会出现问题。因此,请避免使用这样的选择器:table.print > tbody > tr + trtr ~ tr。同时,避免使用伪类如 :first-child:last-child:nth-child 等来定位特定的行。一般来说,如果您想以不同的方式为某一行设置样式,最好给它一个独立的类。 - DoctorDestructo
1
@VoidKing 我们有点偏离主题了,但是根据 这个代码审查 底部的消息,我得到的印象是,我们的英雄Chromium开发人员Rob Hogan(正在努力实现重复标题)已经为表格行实现了 page-break-inside: avoid;!在我的Chrome中仍然无法正常工作,但也许很快就会。你已经掌握了我是如何做到的,只是Chrome必须通过CSS“告诉”它不要打破表格副本。我使用以下CSS来实现:display: inline-table; page-break-inside: avoid; - DoctorDestructo
显示剩余23条评论

40

我认为这是Chrome中的漏洞


你说得对...在我发现了你提供的同样的链接后,我已经放弃了让显示器在Chrome上按预期工作的想法。 - Stephen M
1
这实际上是一项Chromium中的错误报告时间为2008-02-07。我们不应该指望它很快就会被修复。 - piotr_cz
2
截至2016年2月,它似乎正在得到一些关注: 2016年2月4日#34 robho...@gmail.com 我正在朝这个方向努力。 - mike nelson
现在已经修复了。 - Alvaro
在Chrome 79.0.3945.130上对我似乎不起作用。 - Oskar Sjöberg

4

现在可以使用jQuery在Chrome中进行打印...请尝试使用以下代码(很抱歉,我忘记了在我修改之前谁是这段代码的创建者 - 我的英语不好:D 哈哈哈)

            <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
            <html>
            <head>
                <title>DOCUMENT TITLE</title>
                <link rel="stylesheet" type="text/css" href="assets/css/bootstrap.css"/>
                <style type="text/css">
                    @media print{
                        table { page-break-after:auto;}
                        tr    { page-break-inside:avoid;}
                        td    { page-break-inside:auto;}
                        thead { display:table-header-group }

                        .row-fluid [class*="span"] {
                          min-height: 20px;
                        }
                    }

                    @page { 
                        margin-top: 1cm;
                        margin-right: 1cm;
                        margin-bottom:2cm;
                        margin-left: 2cm;';
                        size:portrait;
                        /*
                        size:landscape;
                        -webkit-transform: rotate(-90deg); -moz-transform:rotate(-90deg);
                        filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
                        */

                    };
                </style>
            </head>
            <body>
                <div id="print-header-wrapper">
                    <div class="row-fluid">HEADER TITLE 1</div>
                    <div class="row-fluid">HEADER TITLE 2</div>
                </div>
                <div class="row-fluid" id="print-body-wrapper">
                    <table class="table" id="table_data">
                        <thead>
                            <tr><th>TH 1</th><th>TH 2</th></tr>
                        </thead>
                        <tbody>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                            <tr><td>TD 1</td><td>TD 2</td></tr>
                        </tbody>
                    </table>
                    <div id="lastDataTable"></div>
                </div>
                <script type="text/javascript">
                    jQuery(document).ready(function()
                    {
                        var printHeader = $('#print-header-wrapper').html();
                        var div_pageBreaker = '<div style="page-break-before:always;"></div>';
                        var per_page = 25;
                        $('#table_data').each(function(index, element)
                        {
                            //how many pages of rows have we got?
                            var pages = Math.ceil($('tbody tr').length / per_page);

                            //if we only have one page no more
                            if (pages == 1) {
                                return;
                            }
                            //get the table we're splutting
                            var table_to_split = $(element);

                            var current_page   = 1;
                            //loop through each of our pages
                            for (current_page = 1; current_page <= pages; current_page++) 
                            {
                                //make a new copy of the table
                                var cloned_table = table_to_split.clone();
                                //remove rows on later pages
                                $('tbody tr', table_to_split).each(function(loop, row_element) {
                                    //if we've reached our max
                                    if (loop >= per_page) {
                                        //get rid of the row
                                        $(row_element).remove();
                                    }
                                });

                                //loop through the other copy
                                $('tbody tr', cloned_table).each(function(loop, row_element) {
                                    //if we are before our current page
                                    if (loop < per_page) {
                                        //remove that one
                                        $(row_element).remove();
                                    }
                                });

                                //insert the other table afdter the copy
                                if (current_page < pages) {
                                    $(div_pageBreaker).appendTo('#lastDataTable');
                                    $(printHeader).appendTo('#lastDataTable');
                                    $(cloned_table).appendTo('#lastDataTable');
                                }

                                //make a break
                                table_to_split = cloned_table;
                            }
                        });
                    });
                </script>
              </body>
            </html>

太棒了...我有时间会去看一下并回报。虽然我已经解决了这个问题。 - Stephen M
看起来不错,但在我的情况下,我正在使用行合并(标题单元格跨越多个tr)。我建议使用自定义类来标记表格的切割位置。 - piotr_cz

2
这是一项增强功能,目前在 Webkit、Blink 和 Vivliostyle 中还不可用,而在其他更“打印导向的格式化程序”(Firefox、IE)中可用。
你可以查看谷歌 Chrome 自版本 4 以来的问题(6 年和 45 个版本之前!),在那里我们可以欣赏到它最近有了一个所有者(2016 年 2 月),甚至似乎正在开发中。
一些也在 W3 上进行了讨论,在那里我们可以感受到对其有用性的关注:

由于在分段断点处重复表头和页脚通常是有用的,我建议我们制定规范要求,即 UA 在表跨越断点时必须重复标题/页脚行。

同时,@DoctorDestructo 和 @thefredzx 的 JS 和 Jquery 代码对我的用户非常有用,因为他们大多数人都没有使用 Firefox 或 IE。
第一个注意到包含此功能的新版本的人,应该在这里通知大家,我们中的许多人会很感激。


1

从我的测试中发现,在Chrome设置display: table-row-group;到thead可以防止出现问题。

例如,如果您尝试在没有样式的情况下打印以下内容,您将看到每个页面顶部的数字,但是如果您添加样式,则仅在oncontextmenu中显示。

<style>
  thead {
      display: table-row-group;
  }
</style>
<table>
  <thead>
    <tr>
      <th>number</th>
    </tr>
  </thead>
  <tbody id="myTbody">
  </tbody>
</table>
<script>
  for (i = 1; i <= 100; i++) {
    document.getElementById("myTbody").innerHTML += "<tr><td>" + i + "</td></tr>";
  }
</script>

0

2019年的备注: 我曾经为此苦苦挣扎了数小时,我的头部有一张图片

<table>
 <thead>
    <tr>
      <td>
        <img ...>
      </td>

这个只在第一页显示的内容。

原因: Chrome添加了一个样式@media print { img { page-break-inside: avoid !important; } }

这导致图片无法显示在第二页及以后的页面!

解决方案: 只需插入一条规则即可。

@media print { thead img { page-break-inside: auto !important; } }

取消激活此浏览器特定规则。


-4

对于公司内部使用的基于浏览器的系统,我建议用户使用Firefox或IE; 对于面向公众的网站,如果用户使用Chrome或类似限制的浏览器(Opera也是如此),我们真的无能为力。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接