印刷ページごとにテーブルのヘッダーを繰り返したいのですが、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 であると思います。
更新2017-03-22:繰り返しテーブルヘッダーがChromeでようやく実装されました!(実際、それらは少し前に実装されたと思います。)つまり、おそらくこのソリューションは不要だもう;列ヘッダーを<thead>
タグに入れるだけで、すべて設定できます。次の場合にのみ以下のソリューションを使用してください。
ソリューション(廃止)
以下のコードは、複数ページのテーブル印刷で見つけた最良の方法を示しています。次の機能があります。
...および次の既知の制限:
<thead>
のみです(明らかに、あなたが最も多く 持っていることが許可されています とにかく)<tfoot>
をサポートしていません(ただし、Chrome互換の実行フッターは 技術的には可能 )<caption>
のみをサポートmargin
を含めることはできません。テーブルの上または下に空白を追加するには、空のdivを挿入し、下マージンを設定しますborder-width
およびline-height
を含む)は、px
になければなりません列の幅は、個々のテーブルセルに幅の値を適用して設定することはできません。セルのコンテンツに列幅を自動的に決定させるか、必要に応じて<col>s
を使用して特定の幅を設定する必要があります
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>
HOW IT WORKS(気にしない場合は、これ以上読みません;必要なものはすべて上記です。)
@Kingsolmnのリクエストごとに、このソリューションの仕組みの説明を以下に示します。厳密には必要ではありませんが、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つだけ指定しました。明らかに、複数ページのテーブルには通常より多くの行があります)
最初に行う必要があるのは、テーブルを一連の小さなテーブルに分割することです。各テーブルには、列ヘッダーの独自のコピーがあります。これらの小さなテーブルをfauxRowsと呼びます。
<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>
FauxRowsは基本的に元のテーブルのクローンですが、1つのデータ行しかありません。 (ただし、テーブルにキャプションがある場合は、一番上のfauxRowのみにキャプションを含める必要があります。)
次に、fauxRowsを作成する必要がありますnbreakable。どういう意味ですか? (注意-これはおそらく改ページ処理で最も重要な概念です。)「Unbreakable」は、2ページに分割できないコンテンツのブロックを表すために使用する用語です*。そのようなブロックが占めるスペース内で改ページが発生すると、ブロック全体が次のページに移動します。 (ここでは非公式に「ブロック」という単語を使用していることに注意してください; not具体的には ブロックレベル要素 を参照しています。)この動作には興味深い副作用があります後で利用します。階層化またはオーバーフローのために最初は隠されていたコンテンツを公開できます。
次のCSS宣言のいずれかを適用することにより、fauxRowsを破壊不能にできます。
page-break-inside: avoid;
display: inline-table;
私は通常両方を使用します。1つ目はこの目的のために作成され、2つ目は古い/非準拠のブラウザーで動作するためです。ただし、この場合、簡単にするために、改ページプロパティに固執します。このプロパティを追加した後、テーブルの外観に変化は見られないことに注意してください。
<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>
FauxRowsが壊れないので、データ行内で改ページが発生すると、添付されたヘッダー行とともに次のページに移動します。そのため、次のページでは常に上部に列ヘッダーがあります。これが目標です。しかし、テーブルはすべての余分なヘッダー行で非常に奇妙に見えます。再び通常のテーブルのように見えるようにするには、必要な場合にのみ表示されるように余分なヘッダーを非表示にする必要があります。
ここでは、各fauxRowを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に注目してください。 2つの理由で追加しました。1つ目は、fauxRowsの間に親テーブルが空白を追加するのを防ぎます。次に、ヘッダーの高さを予測可能にします。これは、JavaScriptを使用して動的に計算しないため、必要です。
ここで、fauxRowsを上にシフトする必要があります。これは負のマージンで行います。しかし、あなたが思うほど簡単ではありません。負のマージンをfauxRowに直接追加すると、fauxRowが次のページにバンプされても有効のままになり、ページの上部でヘッダーがクリップされます。負のマージンを残す方法が必要です。
これを達成するために、最初の後に各fauxRowの上に空のdivを挿入し、それに負のマージンを追加します。 (ヘッダーは常に表示される必要があるため、最初のfauxRowはスキップされます。)マージンは別の要素にあるため、次のページまでfauxRowに追従せず、ヘッダーはクリップされません。これらの空の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>
これで完了です!画面上では、テーブルは通常のように見えるはずです。上部には列ヘッダーのセットが1つだけあります。印刷では、実行中のヘッダーがあります。
なぜコンテナdivの束ではなく親テーブルを使用したのか疑問に思っているのなら、Chrome/webkitには、divで囲まれた壊れないブロックが原因でコンテナを次のページに運ぶバグがあるためです。 headerHiderもコンテナ内にあるため、想定どおりに取り残されず、ヘッダーがクリップされます。このバグは、unbreakableブロックがdivの最上位要素で、高さがゼロでない場合にのみ発生します。
このチュートリアルの作成中に回避策を発見しました。headerHiderでheight: 0;
を明示的に設定し、ゼロ以外の高さの空の子divを指定するだけです。その後、divコンテナを使用できます。ただし、親テーブルはより徹底的にテストされており、fauxRowsを1つのテーブルに戻すことでセマンティクスをある程度改善するため、親テーブルを使用することを好みます。
EDIT:JavaScriptで生成されたマークアップは、各fauxRowを個別のコンテナーテーブルに配置し、 "fauxRow" classNameをそのコンテナー(コンテナー)に割り当てるという点でわずかに異なることに気付きました。これはフッターサポートに必要です。フッターサポートはいつか追加するつもりでしたが、一度も追加しませんでした。 JSを更新する場合、テーブルを使用するためのセマンティクスの正当化が適用されないため、divコンテナーへの切り替えを検討するかもしれません。
*壊れないブロックcanが2つのページに分割される状況が1つあります。それは、印刷可能領域の高さを超える場合です。このシナリオは避けてください。あなたは本質的にブラウザに不可能なことをするように頼んでおり、それは出力にいくつかの非常に奇妙な影響を与える可能性があります。
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>
これは、他の「印刷指向のフォーマッター」(Firefox、IE)ではなく、Webkit、Blink、Vivliostyleではまだ利用できない拡張機能です。
Googleの問題Chromeバージョン4以降(6年前と45バージョン前!) )を確認できます。ここで、最近所有者になったことに感謝します(2016年2月)、誰がそれに取り組んでいるようです。
いくつかの話 W3でも開催されました ここで、その有用性に対する懸念に感謝することができます:
断片化の切れ目でテーブルのヘッダーとフッターを繰り返すことは一般的に便利なことなので、規範的な要件を設け、UAがテーブルが切れ目をまたぐときにヘッダー/フッターの行を繰り返さなければならないと言います。
一方、@ DoctorDestructoと@thefredzxからのJSとJqueryコードは、FirefoxもIE(それらのほとんど))を使用していないユーザーにとって非常に役立ちました。
この機能を含む新しいバージョンを知っている最初の人は、ここでそれに気付くはずです、私たちの多くは感謝するでしょう。
chrome設定display: table-row-group;
theadに問題の発生を停止します。
たとえば、スタイルなしで以下を印刷しようとすると、各ページの上部に番号が表示されますが、スタイルを追加するとコンテキストメニューにのみ表示されます。
<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>