web打印的奇技淫巧
背景
- 公司需要做一个报告打印,内容过长会分为多张纸打印,且每页都必须要有固定的
页眉、页脚
奇技淫巧一:控制台以打印媒体类型展示
- 在
devtool->渲染/render->模拟CSS媒体类型中拥有打印模式,它可以显示你在打印下的样式
@media print {
.header,
.footer {
position: fixed;
}
}
奇技淫巧二:打印背景
- 默认不打印背景
- 设置打印背景
实现
<button onClick={print}>打印</button>
<StyleWrapper className="wrapper" ref={wrapperRef}>
<div className="header" ref={headerRef}>
日记:GPT的一天
</div>
<div className="content">
// ...
</div>
<table className="print-table" ref={tableRef}>
<thead></thead>
<tbody></tbody>
<tfoot></tfoot>
</table>
<div className="footer" ref={footerRef}>
作者:GPT3.5
</div>
</StyleWrapper>
<iframe
ref={iframeRef}
title="打印iframe容器"
width="100%"
height="600px"
/>
</>
- 🤔思路:
.content用于html展示的数据,table.print-table(display: none)用于打印时显示,将content拷贝一份到table中,再将content隐藏,table显示就可以在打印预览中显示了 - 使用了
position: fixed后头部和底部占位的解决方案:使用table的thead、tfoot,他们可以在每一页中都实现占位的作用
🪧 至于原理,希望大佬们知道的回复我,我在网上一直没有找到这块相关的内容...
/**
* 打印核心逻辑
*/
function print() {
if (
!tableRef.current ||
!wrapperRef.current ||
!headerRef.current ||
!footerRef.current ||
!iframeRef.current
)
return;
const contentEl = wrapperRef.current.querySelector(
".content"
) as HTMLDivElement;
const theadEl = tableRef.current.tHead as HTMLTableSectionElement;
const tfootEl = tableRef.current.tFoot as HTMLTableSectionElement;
const tbodyEl = tableRef.current.tBodies[0];
tbodyEl.appendChild(contentEl.cloneNode(true)); // clone主体部分copy到table里
contentEl.style.display = "none";
// 设置thead、tfoot高度,占位
const headerHeight = getElHeight(headerRef);
const footerHeight = getElHeight(footerRef);
theadEl.style.height = `${headerHeight + 10}px`;
tfootEl.style.height = `${footerHeight + 10}px`;
// 拷贝打印内容到iframe
const header = document.head.innerHTML;
console.log(wrapperRef.current);
const copyNode = wrapperRef.current.cloneNode(true);
const iframeDoc = iframeRef.current.contentDocument as Document;
const iframeWin = iframeRef.current.contentWindow as Window;
iframeDoc.head.innerHTML = header;
iframeDoc.body.appendChild(copyNode);
iframeWin.focus();
iframeWin.print();
// 复原
contentEl.style.display = "block";
iframeDoc.head.innerHTML = "";
iframeDoc.body.innerHTML = "";
}
给孩子一点鼓励
后续追加的细节
- 2023-06-05 通过限制网络速度发现,浏览器并不会等待内部异步元素加载(如:图片)完毕后,调出打印界面
问题:可以看到这个logo并没有下载完毕,可能在高带宽场景复现不了,这方面还是要注意一下
解决方案:对于高延迟异步元素打印时,等待"完成事件"钩子(onload...)触发后再进行打印
- 2023-06-06 如果希望thead、tfooter每页都显示,一定要在thead中定义tr元素,否则不会每页生效
参考地址:W3C官网:table元素
-
⚠️ 2023-09-16 发现了chrome print对position: fixed脱离文档流的width: 100%的算法更改。
-
结论:为了不同版本兼容性,应该将table内容强制设置为100%。 -
当chrome打印table内容要大于A4纸宽度时,会缩放显示所有A4纸的内容
-
Chrome v115及以下,width: 100%表示显示的最大宽度。比如:table宽1500px,则100% === 1500px(高度同理)

-
chrome v116-v119左右,width: 100%表示A4纸的最大宽度。假设,A4宽度为900px,table宽1500px,则100% === 900px(高度同理)
下图1:chrome v119版本下,table宽为1100px,width: 100%实际上只有A4纸大小的问题!
下图2:chrome v119版本下,table宽为100%,正常显示
-
<!-- 测试html,可以自行测试 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Print Bug🐛</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
width: 100%;
}
.print-iframe {
width: 100%;
height: 100%;
border: none;
border-top: 1px solid black;
border-bottom: 1px solid black;
}
@media print {
#print {
width: 100%;
background-color: antiquewhite;
}
.footer,
.header {
position: fixed;
left: 0;
width: 100%;
height: 40px;
line-height: 40px;
opacity: 0.5;
text-align: center;
font-size: 24px;
font-weight: bolder;
}
.header {
top: 0;
background-color: brown;
}
.footer {
bottom: 0;
background-color: paleturquoise;
}
table {
width: 100%;
}
.content {
/*
注释这行,tbody的内容宽度为写死的1100px,此宽度要远大于A4的宽度的像素值
只用强制将该宽度设置为100%即可解决
*/
/* width: 100% !important; */
}
}
</style>
</head>
<body>
<button onclick="print()">Print</button>
<div id="print">
<div class="header">Header</div>
<div class="content" style="width: 1100px;">
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
<div>Tenderloin ham boudin tongue sausage venison short ribs sirloin, kielbasa beef ribs. Strip steak shank
bresaola
salami spare ribs kielbasa fatback, cow t-bone flank leberkas sirloin. Jowl pork belly ribeye, corned beef
sirloin
chicken salami tail. Rump swine ham shank corned beef short loin, speck turkey pancetta shankle frankfurter.
Pancetta tail fatback, ground round brisket biltong frankfurter turkey. Ham hock chicken strip steak, salami
short
ribs beef ribs pork sirloin pastrami pork loin turducken rump brisket andouille.
</div>
</div>
<table style="display: none; background-color: pink; width: 100%;">
<thead>
<tr>
<td style="width: 100%; height: 50px; background-color: skyblue;"></td>
</tr>
</thead>
<tbody>
<tr>
<td id="tableContent"></td>
</tr>
</tbody>
<tfoot>
<tr>
<td style="width: 100%; height: 50px; background-color: rosybrown;"></td>
</tr>
</tfoot>
</table>
<div class="footer">
Footer
</div>
</div>
</body>
<script>
function print() {
const existIframe = document.querySelector(".print-iframe");
if (existIframe) {
existIframe.contentWindow.print();
return;
}
const content = document.querySelector("#print").cloneNode(true);
const tableEl = content.querySelector("table");
const tbodyContentEl = tableEl.querySelector("#tableContent");
const contentEl = content.querySelector(".content");
const contentCp = contentEl.cloneNode(true);
contentEl.style.display = "none";
tableEl.style.display = "table";
tbodyContentEl.appendChild(contentCp);
const iframe = document.createElement("iframe");
iframe.className = "print-iframe";
document.body.appendChild(iframe);
const iframeDoc = iframe.contentDocument;
const iframeWin = iframe.contentWindow;
iframeDoc.body.appendChild(content);
iframeDoc.head.innerHTML = document.head.innerHTML;
iframeWin.print();
}
</script>
</html>
🤔️ 目前该问题没有找到明显的参考地址,且起码会发生在Chrome v115+以上。(如果有大佬查到W3C来源或Chrome功能的来源可以分享一下)
有关打印CSS样式
- 兼容性:v Chrome 1+
page-break-inside // 针对元素内的分页符
// avoid 避免, always 总是, auto 自动
.div { page-break-inside: avoid } // 该div元素不允许被分割成两页显示
page-break-before // 针对元素之前的分页符
page-break-after // 针对元素之后的分页符
-
兼容性:chrome v50+
-
以上样式都可以被
break-*替换
break-insde
break-before
break-after
@page:只能包含page-properties属性和margin at-rules
// 设置所有页面
@page {
size: a4;
margin-top: 4pt;
}
@page :first {} // 设置第一页
@page :left {} // 设置所有奇数页
@page :right {} // 设置所有偶数页
// 设置自定义页面
// @page [custom-name] {}
@page page1 {
size: a4;
@top-middle {
content: "Chapter";
}
}
.main {
page: page1;
}
参考:CSS page属性
margin at-rules:只能包含page-margin属性
@page {
@top-left {
content: "内容"
}
}
更多margin at-rules参考:MDN margin at-rules属性