【问题标题】:Print subtotals on each page with HTML/CSS/JS maybe XSLT?使用 HTML/CSS/JS 或 XSLT 在每个页面上打印小计?
【发布时间】:2015-01-15 08:46:07
【问题描述】:

我想使用开放标准打印报告,此报告需要在每一页的末尾有小计。

线程 Use of XSL-FO, CSS3 instead of CSS2 to create Paginated documents like PDF? 建议使用 CSS3 的 HTML 是要走的路。

现在我遇到的问题是使用 HTML 或 CSS 或 JS 确定将填充 A4 页面的位置,以便出现分页符。

有 CSS 分页符选择器:

page-break-before: always | avoid — always/avoid page breaks before the item
page-break-after: always | avoid — always/avoid page breaks after the item
page-break-inside: always | avoid — always/avoid page breaks in the middle of the item

但是这些对于每个页面上的小计来说并不是很有用,因为我不知道页面什么时候会填满,以便我可以在其中放置一个标签并应用这样的 CSS。

HTML TABLE 标签支持某种听起来很有前途的表格页眉和页脚:

表格行可以分为表格头、表格脚和一个或 更多表格主体部分,使用 THEAD、TFOOT 和 TBODY 元素, 分别。这种划分使用户代理能够支持滚动 独立于表头和表脚的表体。当长 表打印,表头和脚信息可能是 在包含表格数据的每个页面上重复。

<TABLE>
<THEAD>
     <TR> ...header information...
</THEAD>
<TFOOT>
     <TR> ...footer information...
</TFOOT>
<TBODY>
     <TR> ...first row of block one data...
     <TR> ...second row of block one data...
</TBODY>
<TBODY>
     <TR> ...first row of block two data...
     <TR> ...second row of block two data...
     <TR> ...third row of block two data...
</TBODY>
</TABLE>

但这又不是很有用,因为相同的页脚会出现在具有相同小计的所有页面上,而不是每个页面上。否则我应该用不同的 TFOOT 和 TBODY 制作一个表格,根据页面何时填充的静态结构......但我无法获得那种信息,只有行数是我在构建 HTML 时可以指定的桌子。我看不到创建它们的方法,因为 A4 尺寸适合它们。

我不知道,也许 XSLT 或 javascript 会有所帮助?是否有针对此问题的开放标准 CSS3、HTML5 或其他我不知道的解决方案?如果没有,任何人都知道解决方法,解决此问题的示例?

【问题讨论】:

    标签: html css printing html-table


    【解决方案1】:

    这可以使用普通的旧 JavaScript 来完成,如下面的代码所示。

    <!DOCTYPE html>
    <html>
      <body>
        <table class="data">
          <caption><b>MY TABLE</b></caption>
          <thead>
            <tr>
              <th>Col 1</th><th>Col 2</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>1<td>1<tr><td>0<td>99<tr><td>1<td>1<tr><td>0<td>99<tr><td>1<td>1<tr><td>0<td>99
            <tr><td>2<td>9<tr><td>2<td>88<tr><td>2<td>0<tr><td>2<td>88<tr><td>2<td>0<tr><td>2<td>88
            <tr><td>3<td>1<tr><td>4<td>77<tr><td>3<td>1<tr><td>4<td>77<tr><td>3<td>1<tr><td>4<td>77
            <tr><td>4<td>8<tr><td>6<td>66<tr><td>4<td>1<tr><td>6<td>66<tr><td>4<td>1<tr><td>6<td>66
            <tr><td>5<td>1<tr><td>8<td>55<tr><td>5<td>1<tr><td>8<td>55<tr><td>5<td>1<tr><td>8<td>55
            <tr><td>6<td>7<tr><td>0<td>44<tr><td>6<td>2<tr><td>0<td>44<tr><td>6<td>2<tr><td>0<td>44
            <tr><td>7<td>1<tr><td>2<td>33<tr><td>7<td>1<tr><td>2<td>33<tr><td>7<td>1<tr><td>2<td>33
            <tr><td>8<td>6<tr><td>4<td>22<tr><td>8<td>3<tr><td>4<td>22<tr><td>8<td>3<tr><td>4<td>22
            <tr><td>9<td>1<tr><td>6<td>11<tr><td>9<td>1<tr><td>6<td>11<tr><td>9<td>1<tr><td>6<td>11
            <tr><td>1<td>0<tr><td>0<td>99<tr><td>1<td>4<tr><td>0<td>99<tr><td>1<td>4<tr><td>0<td>99
            <tr><td>2<td>1<tr><td>2<td>88<tr><td>2<td>1<tr><td>2<td>88<tr><td>2<td>1<tr><td>2<td>88
            <tr><td>3<td>1<tr><td>4<td>77<tr><td>3<td>1<tr><td>4<td>77<tr><td>3<td>1<tr><td>4<td>77
            <tr><td>4<td>5<tr><td>6<td>66<tr><td>4<td>5<tr><td>6<td>66<tr><td>4<td>5<tr><td>6<td>66
            <tr><td>5<td>1<tr><td>8<td>55<tr><td>5<td>1<tr><td>8<td>55<tr><td>5<td>1<tr><td>8<td>55
            <tr><td>6<td>4<tr><td>0<td>44<tr><td>6<td>6<tr><td>0<td>44<tr><td>6<td>6<tr><td>0<td>44
            <tr><td>7<td>1<tr><td>2<td>33<tr><td>7<td>1<tr><td>2<td>33<tr><td>7<td>1<tr><td>2<td>33
            <tr><td>8<td>3<tr><td>4<td>22<tr><td>8<td>7<tr><td>4<td>22<tr><td>8<td>7<tr><td>4<td>22
            <tr><td>9<td>1<tr><td>6<td>11<tr><td>9<td>1<tr><td>6<td>11<tr><td>9<td>1<tr><td>6<td>11
            <tr><td>1<td>2<tr><td>0<td>99<tr><td>1<td>8<tr><td>0<td>99<tr><td>1<td>8<tr><td>0<td>99
            <tr><td>2<td>1<tr><td>2<td>88<tr><td>2<td>1<tr><td>2<td>88<tr><td>2<td>1<tr><td>2<td>88
            <tr><td>3<td>0<tr><td>4<td>77<tr><td>3<td>9<tr><td>4<td>77<tr><td>3<td>9<tr><td>4<td>77
            <tr><td>4<td>1<tr><td>6<td>66<tr><td>4<td>1<tr><td>6<td>66<tr><td>4<td>1<tr><td>6<td>66
            <tr><td>5<td>1<tr><td>8<td>55<tr><td>5<td>0<tr><td>8<td>55<tr><td>5<td>0<tr><td>8<td>55
            <tr><td>6<td>1<tr><td>0<td>44<tr><td>6<td>1<tr><td>0<td>44<tr><td>6<td>1<tr><td>0<td>44
            <tr><td>7<td>0<tr><td>2<td>33<tr><td>7<td>1<tr><td>2<td>33<tr><td>7<td>1<tr><td>2<td>33
            <tr><td>8<td>1<tr><td>4<td>22<tr><td>8<td>1<tr><td>4<td>22<tr><td>8<td>1<tr><td>4<td>22
            <tr><td>9<td>0<tr><td>6<td>11<tr><td>9<td>2<tr><td>6<td>11<tr><td>9<td>2<tr><td>6<td>11
            <tr><!--Use this row for on-screen totals if needed; otherwise, leave it empty.-->
          </tbody>
        </table>
      </body>
    </html>
    
    <style>
      @media screen {
        .print {
          display: none; /*Prevents print version of table from showing on screen*/
        }
      }
      @media print {
        .data {
          display: none; /*Prevents screen version of table from showing in print*/
        }
        .print {
          display: block;
        }
        .print > .data {
          display: inline-table; /*Prevents page breaks better than page-break-inside: avoid;*/
          vertical-align: top;
        }
        /*The following rule makes rows opaque in IE even if background colors are disabled.*/
        .print.fixIE > .data > thead > tr > th:after,
        .print.fixIE > .data > tbody > tr:first-child > td:after {
          display: block;
          border-bottom: 18pt solid white; /*Border-width = line-height*/
          margin-top: -18pt; /*Negative line-height*/
          margin-right: -.5em; /*Negative td padding-right*/
          margin-left: -.5em; /*Negative td padding-left*/
          content: "";
        }
        .overlap {
          margin-bottom: -20pt; /*Negative row height (including borders)*/
        }
      }
      .data {
        table-layout: fixed; /*Columns must have fixed widths! Set with <col>s, if needed.*/
        width: 100%;
        border-spacing: 0;
        white-space: nowrap;
        font-size: 12pt;
        line-height: 18pt; /*If you change this, other CSS values must also be changed!*/
        border-right: 1pt solid black;
      }
      .data > thead > tr > th {
        border-top: 1pt solid black;
        border-left: 1pt solid black;
        background: white;
        padding: 0 .5em 0 .5em;
      }
      .data > tbody > tr > td {
        border-top: 1pt solid black; /*If you change this, .overlap must also be changed!*/
        border-left: 1pt solid black;
        background: white;
        padding: 0 .5em 0 .5em; /*If you change this, other CSS values must also be changed!*/
      }
      .data > tbody > tr:last-child > td {
        border-bottom: 1pt solid black; /*If you change this, .overlap must also be changed!*/
      }
      .data > tbody {
        text-align: right;
      }
    </style>
    
    <script>
      //This function takes two arguments:
      //1) a reference to a table element
      //2) an array of column indexes indicating which columns have numbers to be totalled.
      function printSubtotals(table, columns) {
        var
          tbody = table.tBodies[0],
          row = tbody.rows[0];
        if(!row)
          return;
        var cellCount = row.cells.length;
        if(!cellCount)
          return;
        var
          subtotals = [],
          rows = table.rows,
          thead = table.tHead,
          caption = table.querySelector('caption'),
          colgroup = table.querySelector('colgroup'),
          emptyTable = table.cloneNode(false),
          emptyRow = row.cloneNode(true),
          printDiv = document.createElement('div'),
          overlap = document.createElement('div'),
          subtotalCount = columns.length,
          rowCount = rows.length - 1,
          cells, subtotalCells, i, r;
        if(colgroup && colgroup.parentNode === table)
          emptyTable.appendChild(colgroup.cloneNode(true));
        emptyTable.appendChild(tbody.cloneNode(false));
        printDiv.className = /MSIE /.test(navigator.userAgent) ? 'print fixIE' : 'print';
        overlap.className = 'overlap';
        for(i = subtotalCount; i--; subtotals.push(0));
        for(i = cellCount; i--; emptyRow.cells[i].innerHTML = '');
        for(r = row.rowIndex; r < rowCount; r++) {
          printDiv.appendChild(overlap.cloneNode(true));
          tbody = printDiv.appendChild(emptyTable.cloneNode(true)).tBodies[0];
          cells = tbody.appendChild(rows[r].cloneNode(true)).cells;
          subtotalCells = tbody.appendChild(emptyRow.cloneNode(true)).cells;
          for(i = subtotalCount; i--;) {
            subtotals[i] += parseFloat(cells[columns[i]].innerHTML);
            subtotalCells[columns[i]].innerHTML = '<b>Total: ' + subtotals[i] + '</b>';
          }
        }
        printDiv.removeChild(printDiv.children[0]);
        tbody = printDiv.children[0].tBodies[0];
        if(caption && caption.parentNode === table)
          tbody.parentNode.insertBefore(caption.cloneNode(true), tbody);
        if(thead)
          tbody.parentNode.insertBefore(thead.cloneNode(true), tbody);
        table.parentNode.insertBefore(printDiv, table);
      }
    
      printSubtotals(document.querySelector('.data'), [0,1]);
    </script>
    

    Javascript 函数所做的是创建一个仅打印版本的表格,其中每一行都附加了一个隐藏的小计行。小计行被下一行覆盖,因此只有在下一行碰到下一页或者它是表中的最后一行时才可见。无论纸张大小如何,这应该适用于几乎任何浏览器。它可能看起来效率低下,但感知到的性能影响可以忽略不计,因为打印表不会在页面加载时呈现。不过,对于有数千行的表,我不会推荐这种技术。

    请注意,上面的代码只是一个概念验证,因此并不是特别健壮。我知道会破坏它的几件事是:

    • 数据列中的空单元格
    • 数据列中的非数字值
    • tbody 单元格中的多行内容
    • tbody 行中的不同单元格计数(例如,有些有 colspan,有些没有)
    • 让单元格内容决定列宽
    • 更改行高或边框宽度而不更改其他相关的 CSS 值

    其中一些事情可以通过对 JavaScript 和 CSS 进行正确的修改来实现。

    【讨论】:

    • 解决这个问题的 JS 代码 sn-p 会很有趣。指向这个或 jsfiddle 示例的工作库将不胜感激。
    • 我原来的答案在 Chrome 和其他 WebKit 浏览器中不起作用,所以我重写了它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-29
    • 1970-01-01
    • 2015-03-11
    • 2017-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多