I believe this is a bug in Chrome.
-------------------
UPDATE 2017-03-22: Repeating table headers have finally been implemented in Chrome! (Actually, I think they were implemented some time ago.) That means you probably don't need this solution anymore; just put your column headers in a <thead>
tag and you should be all set. Use the solution below only if:
- you encounter show-stopping bugs in Chrome's implementation,
- you need the "bonus features", or
- you need to support some oddball browser that still doesn't support repeating headers.
SOLUTION (obsolete)
The code below demonstrates the best method I've found for multi-page table printing. It has the following features:
- Column headers repeat on each page
- No need to worry about paper size or how many rows will fit-- the browser handles everything automatically
- Page breaks occur only between rows
- Cell borders are always fully closed
- If a page break occurs near the top of the table, it won't leave behind an orphaned caption or column headers with no data attached (a problem which isn't limited to just Chrome)
- Works in Chrome! (and other Webkit-based browsers like Safari and Opera)
... and the following known limitations:
- Only supports 1
<thead>
(which is apparently the most you're allowed to have anyway) - Doesn't support
<tfoot>
(though Chrome-compatible running footers are technically possible) - Only supports top-aligned
<caption>
- Table can't have top or bottom
margin
; to add white space above or below the table, insert an empty div and set a bottom margin on it - Any CSS size values that affect height (including
border-width
andline-height
) must be inpx
-
Column widths can't be set by applying width values to individual table cells; you should either let cell content automatically determine column width, or use
<col>s
to set specific widths if needed -
Table can't (easily) be changed dynamically after the JS has run
THE CODE
<!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 (If you don't care, read no further; everything you need is above.)
Per @Kingsolmn's request, below is an explanation of how this solution works. It doesn't cover the JavaScript, which isn't strictly required (though it does make this technique much easier to use). Instead, it focuses on the generated HTML structures and associated CSS, which is where the real magic happens.
Here's the table we'll be working with:
<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>
(To save space, I gave it only 3 data rows; obviously, a multipage table would usually have more)
The first thing we need to do is split the table into a series of smaller tables, each with its own copy of the column headers. I call these smaller tables 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>
The fauxRows are essentially clones of the original table, but with only one data row apiece. (If your table has a caption, though, only the top fauxRow should include it.)
Next we need to make the fauxRows unbreakable. What does that mean? (Pay attention-- this is probably the most important concept in page break handling.) "Unbreakable" is the term I use to describe a block of content that can't be split between two pages*. When a page break occurs within the space occupied by such a block, the whole block moves to the next page. (Note that I'm using the word "block" informally here; I'm not referring specifically to block level elements.) This behavior has an interesting side-effect that we'll make use of later on: it can expose content that was initially hidden due to layering or overflow.
We can make the fauxRows unbreakable by applying either of the following CSS declarations:
page-break-inside: avoid;
display: inline-table;
I usually use both, because the first is made for this purpose and the second works in older/noncompliant browsers. In this case, though, for simplicity's sake, I'll stick to the page-break property. Note that you will not see any change in the table's appearance after adding this property.
<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>
Now that the fauxRows are unbreakable, if a page break occurs within a data row, it will shift to the next page along with its attached header row. So the next page will always have column headers at the top, which is our goal. But the table looks very strange now with all the extra header rows. To make it look like a normal table again, we need to hide the extra headers in such a way that they'll appear only when needed.
What we're going to do is put each fauxRow in a container element with overflow: hidden;
and then shift it upward so that the headers get clipped by the top of the container. This will also move the data rows back together so that they appear contiguous.
Your first instinct might be to use divs for the containers, but we're going to use the cells of a parent table instead. I'll explain why later, but for now, let's just add the code. (Once again, this will not affect the table's appearance.)
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>
Notice the CSS above the table markup. I added it for two reasons: first, it prevents the parent table from adding white space between the fauxRows; second, it makes the header height predictable, which is necessary since we're not using JavaScript to calculate it dynamically.
Now we just need to shift the fauxRows upward, which we'll do with negative margins. But it's not as simple as you might think. If we add a negative margin directly to a fauxRow, it will remain in effect when the fauxRow gets bumped to the next page, causing the headers to get clipped by the top of the page. We need a way to leave the negative margin behind.
To accomplish this, we'll insert an empty div above each fauxRow after the first and add the negative margin to it. (The first fauxRow is skipped because its headers should always be visible.) Since the margin is on a separate element, it won't follow the fauxRow to the next page, and the headers won't be clipped. I call these empty divs 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>
That's it, we're done! On screen, the table should now look normal, with only one set of column headers at the top. In print, it should now have running headers.
If you were wondering why we used a parent table instead of a bunch of container divs, it's because Chrome/webkit has a bug that causes a div-enclosed unbreakable block to carry its container to the next page with it. Since the headerHider is also in the container, it won't get left behind like it's supposed to, which leads to clipped headers. This bug only happens if the unbreakable block is the topmost element in the div with a non-zero height.
I did discover a workaround while writing this tutorial: you just have to explicitly set height: 0;
on the headerHider and give it an empty child div with a non-zero height. Then you can use a div container. I still prefer to use a parent table, though, because it has been more thoroughly tested, and it salvages the semantics to some extent by tying the fauxRows back together into a single table.
EDIT: I just realized that the JavaScript-generated markup is slightly different in that it puts each fauxRow in a separate container table, and assigns the "fauxRow" className to it (the container). This would be required for footer support, which I intended to add someday but never did. If I were to update the JS, I might consider switching to div containers since my semantic justification for using a table doesn't apply.
* There is one situation in which an unbreakable block can be split between two pages: when it exceeds the height of the printable area. You should try to avoid this scenario; you're essentially asking the browser to do the impossible, and it can have some very strange effects on the output.
-------------------
이제 jQuery를 사용하여 크롬으로 인쇄 할 수 있습니다 ....이 코드를 시도해보세요 (수정하기 전에이 코드를 만든 사람이 누구인지 잊어 버려 죄송합니다. 제 영어 언어가 좋지 않습니다 : D hehehe)
<!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>
-------------------
This is an enhancement still not available in Webkit, Blink, and Vivliostyle, rather than in other more "print oriented formatters" (Firefox, IE).
여기에서 버전 4 (6 년 및 45 개 버전 전!) 이후의 Google Chrome 문제를
확인할 수 있습니다 . 최근 (2016 년 2 월) 소유자가 작업중인 것으로 보이는 소유자가 있음을 감사 할 수 있습니다.
W3
에서도 유용성에 대한 우려를 감사 할 수있는 몇 가지 강연 이있었습니다 .
조각화 나누기에서 테이블 머리글과 바닥 글을 반복하는 것이 일반적으로 유용한 것이므로, 표준 요구 사항을 만들고 테이블이 나누기에 걸쳐있을 때 UA가 머리글 / 바닥 글 행을 반복해야한다고 제안합니다.
한편, @DoctorDestructo 및 @thefredzx의 JS 및 Jquery 코드는 Firefox 나 IE (대부분)를 사용하지 않는 사용자에게 정말 유용했습니다.이 기능이 포함 된 새 버전을 처음으로 알게 된 사람은 여기에서 확인해야합니다. 많은 사람들이 감사 할 것입니다.-------------------
크롬 설정의 테스트에서
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>
-------------------
2019 년 발언 : 몇 시간 동안 고군분투했고 머리글에 이미지가있었습니다.
<table>
<thead>
<tr>
<td>
<img ...>
</td>
첫 페이지에만 표시되었습니다.
원인 :
Chrome은 스타일을 추가합니다.
@media print { img { page-break-inside: avoid !important; } }
이로 인해 이미지가 2 페이지 이상에 표시되지 않았습니다!
솔루션 :
따라서 규칙을 삽입하십시오.
@media print { thead img { page-break-inside: auto !important; } }
이 브라우저 특정 규칙을 비활성화합니다.-------------------
사내 브라우저 기반 시스템의 경우 사용자에게 대신 파이어 폭스 나 IE를 사용하라고 조언합니다. 일반인을 대상으로하는 웹 사이트의 경우 사용자가 크롬이나 비슷한 제한이있는 브라우저 (opera)를 사용하는 경우 실제로 아무것도 할 수 없다고 생각합니다.
출처
https://stackoverflow.com/questions/22089785